@peekling/adapter-codex-pet 0.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/AUTHORS +8 -0
- package/LICENSE +202 -0
- package/LICENSING.md +20 -0
- package/NOTICE +6 -0
- package/README.md +168 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +281 -0
- package/package.json +57 -0
package/AUTHORS
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
The Peekling Codex Pet Adapter was originally designed and developed by Prajwal S. Venkateshmurthy (https://prajwal.me).
|
|
2
|
+
|
|
3
|
+
Other contributors are recorded in the repository history.
|
|
4
|
+
|
|
5
|
+
This is a factual authorship record. Licensing terms and applicable
|
|
6
|
+
notice-retention requirements are in LICENSE and NOTICE. Redistributions must
|
|
7
|
+
comply with Section 4 of the Apache License 2.0, including its applicable
|
|
8
|
+
notice-retention requirements.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/LICENSING.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Licensing and attribution
|
|
2
|
+
|
|
3
|
+
The Peekling Codex Pet Adapter code and documentation are licensed under
|
|
4
|
+
Apache-2.0. The license allows personal and commercial use, modification,
|
|
5
|
+
distribution, and sublicensing subject to its terms.
|
|
6
|
+
|
|
7
|
+
Redistributions must comply with Section 4 of the Apache License 2.0, including
|
|
8
|
+
its applicable notice-retention requirements. When a distribution includes a
|
|
9
|
+
`NOTICE` file, Section 4 describes how readable copies of applicable NOTICE
|
|
10
|
+
attributions may be provided. Apache-2.0 does not require every website using
|
|
11
|
+
Peekling to display a visible attribution.
|
|
12
|
+
|
|
13
|
+
`AUTHORS` is a factual authorship record and does not add a license condition.
|
|
14
|
+
Source character artwork retains its source license. The Peekling name, logo,
|
|
15
|
+
official mascot, and other distinctive brand identity are outside this code
|
|
16
|
+
license except as allowed by applicable law or separate permission. See Section
|
|
17
|
+
6 of Apache-2.0.
|
|
18
|
+
|
|
19
|
+
The Apache License text controls if this summary conflicts with it. This summary
|
|
20
|
+
is informational and is not legal advice.
|
package/NOTICE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# `@peekling/adapter-codex-pet`
|
|
2
|
+
|
|
3
|
+
Use this optional adapter when an application already has an unchanged Codex Pet
|
|
4
|
+
v2 bundle and documented rights to use its character artwork. Native Peekling
|
|
5
|
+
Packs do not need this package.
|
|
6
|
+
|
|
7
|
+
The adapter converts `pet.json`, its fixed atlas, and a Peekling licensing
|
|
8
|
+
sidecar into Peekling's normalized Pack shape. It does not repack source files,
|
|
9
|
+
evaluate bundled scripts, or grant redistribution rights.
|
|
10
|
+
|
|
11
|
+
## Prerequisites and installation
|
|
12
|
+
|
|
13
|
+
- Node.js 22.14.0 or newer for package installation and tooling
|
|
14
|
+
- A Codex Pet v2 `pet.json`
|
|
15
|
+
- A 1536×2288 atlas with transparency
|
|
16
|
+
- A valid SHA-256 for the exact atlas bytes
|
|
17
|
+
- A sidecar that records license and provenance
|
|
18
|
+
|
|
19
|
+
Install the adapter with the runtime that will consume its output:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install @peekling/runtime @peekling/adapter-codex-pet
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Inputs
|
|
26
|
+
|
|
27
|
+
| Input | Required shape | Purpose |
|
|
28
|
+
| -------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------- |
|
|
29
|
+
| `pet` | Parsed Codex Pet v2 `pet.json` with `spriteVersionNumber: 2` | Supplies the source character name and supported timing metadata |
|
|
30
|
+
| `sidecar` | Peekling format 1 licensing record | Supplies the license, author, source, and redistribution-rights statement |
|
|
31
|
+
| `atlas` | Metadata for the exact 1536×2288 source atlas | Supplies filename, MIME type, geometry, transparency, and SHA-256 |
|
|
32
|
+
| `allowSelfHostedPng` | Optional boolean | Allows `spritesheet.png` instead of the default `spritesheet.webp` path |
|
|
33
|
+
|
|
34
|
+
WebP is the default Codex Pet v2 path. PNG is accepted only when the host sets
|
|
35
|
+
`allowSelfHostedPng: true`, uses the exact `spritesheet.png` filename, and
|
|
36
|
+
declares `image/png`. The adapter does not convert between those formats.
|
|
37
|
+
|
|
38
|
+
## Adapt a bundle
|
|
39
|
+
|
|
40
|
+
This example defines every input. Replace the all-zero digest with the SHA-256
|
|
41
|
+
of the atlas bytes before using the result at runtime.
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { adaptCodexPetV2 } from "@peekling/adapter-codex-pet";
|
|
45
|
+
|
|
46
|
+
const pet = {
|
|
47
|
+
spriteVersionNumber: 2,
|
|
48
|
+
name: "review-companion",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const sidecar = {
|
|
52
|
+
format: 1,
|
|
53
|
+
adapter: "codex-pet-v2",
|
|
54
|
+
license: "CC-BY-4.0",
|
|
55
|
+
provenance: {
|
|
56
|
+
author: "Artist name",
|
|
57
|
+
source: "Original Codex Pet v2 bundle",
|
|
58
|
+
rights: "Licensed by the named author for redistribution",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const atlas = {
|
|
63
|
+
fileName: "spritesheet.webp",
|
|
64
|
+
sha256: "0".repeat(64), // Placeholder. Hash the exact atlas bytes.
|
|
65
|
+
width: 1536,
|
|
66
|
+
height: 2288,
|
|
67
|
+
mimeType: "image/webp",
|
|
68
|
+
hasAlpha: true,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const normalized = adaptCodexPetV2({ pet, sidecar, atlas });
|
|
72
|
+
console.log(normalized.displayName, Object.keys(normalized.states).length);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Expected result: `normalized` contains the fixed Codex v2 States, directional
|
|
76
|
+
mapping, source timing, atlas metadata, and provenance needed by Peekling's
|
|
77
|
+
normalized Pack contract. Invalid geometry, missing transparency, unsafe data,
|
|
78
|
+
or incomplete licensing metadata throws a `TypeError` with a field-specific
|
|
79
|
+
message.
|
|
80
|
+
|
|
81
|
+
## Pass the result to the runtime
|
|
82
|
+
|
|
83
|
+
The normalized Pack is valid runtime input. When it is inline, use `atlasUrl` to
|
|
84
|
+
tell the runtime where the byte-identical source atlas is hosted. This Vite
|
|
85
|
+
example also emits the required runtime stylesheet.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
import { hatch } from "@peekling/runtime";
|
|
89
|
+
import peeklingStyles from "@peekling/runtime/peekling.css?url";
|
|
90
|
+
|
|
91
|
+
const companion = hatch({
|
|
92
|
+
pack: normalized,
|
|
93
|
+
atlasUrl: "/peeklings/review-companion/spritesheet.webp",
|
|
94
|
+
styles: { url: peeklingStyles },
|
|
95
|
+
plan: {
|
|
96
|
+
baseline: {
|
|
97
|
+
channels: ["state"],
|
|
98
|
+
state: { state: "idle" },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await companion.ready;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The atlas served at `atlasUrl` must match the SHA-256 supplied to the adapter.
|
|
107
|
+
The runtime verifies those bytes before decoding the image.
|
|
108
|
+
|
|
109
|
+
## Use a self-hosted PNG
|
|
110
|
+
|
|
111
|
+
Set the opt-in flag and keep the filename and MIME type aligned:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const normalizedPng = adaptCodexPetV2({
|
|
115
|
+
pet,
|
|
116
|
+
sidecar,
|
|
117
|
+
allowSelfHostedPng: true,
|
|
118
|
+
atlas: {
|
|
119
|
+
fileName: "spritesheet.png",
|
|
120
|
+
sha256: "0".repeat(64), // Placeholder. Hash the exact PNG bytes.
|
|
121
|
+
width: 1536,
|
|
122
|
+
height: 2288,
|
|
123
|
+
mimeType: "image/png",
|
|
124
|
+
hasAlpha: true,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Expected result: the adapter accepts the PNG metadata but still leaves hosting,
|
|
130
|
+
delivery, and byte verification to the application and runtime.
|
|
131
|
+
|
|
132
|
+
## Data and trust boundary
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
pet.json + atlas metadata + licensing sidecar
|
|
136
|
+
|
|
|
137
|
+
adaptCodexPetV2
|
|
138
|
+
|
|
|
139
|
+
normalized Pack
|
|
140
|
+
|
|
|
141
|
+
hatch
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
In prose, the adapter snapshots the three data inputs, validates the fixed Codex
|
|
145
|
+
v2 contract, and returns a serializable normalized Pack. The host then passes
|
|
146
|
+
that Pack through the ordinary runtime boundary.
|
|
147
|
+
|
|
148
|
+
Inherited fields, accessors, `toJSON`, cycles, excessive depth or keys, and
|
|
149
|
+
throwing Proxy reflection fail during adaptation. Records must use the current
|
|
150
|
+
realm's ordinary `Object.prototype` or a null prototype. JavaScript cannot
|
|
151
|
+
inspect a Proxy without invoking reflection traps, so the adapter contains a
|
|
152
|
+
throwing trap but cannot promise that no trap runs.
|
|
153
|
+
|
|
154
|
+
Interoperability is best-effort because an external source format can change.
|
|
155
|
+
The adapter supports the fixed Codex Pet v2 geometry and row model documented
|
|
156
|
+
here. It does not infer support for later source versions.
|
|
157
|
+
|
|
158
|
+
## Related documentation
|
|
159
|
+
|
|
160
|
+
- [Configure Peekling](../../docs/configuration.md)
|
|
161
|
+
- [`@peekling/runtime`](../runtime/README.md)
|
|
162
|
+
- [`@peekling/cli` Codex import](../cli/README.md)
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
Apache-2.0. Source character artwork retains its source license. See
|
|
167
|
+
[licensing and attribution](LICENSING.md) for notice retention, authorship, and
|
|
168
|
+
brand boundaries.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export declare const CODEX_V2_WIDTH = 1536;
|
|
2
|
+
export declare const CODEX_V2_HEIGHT = 2288;
|
|
3
|
+
export declare const CODEX_V2_COLUMNS = 8;
|
|
4
|
+
export declare const CODEX_V2_ROWS = 11;
|
|
5
|
+
export declare const CODEX_V2_CELL_WIDTH = 192;
|
|
6
|
+
export declare const CODEX_V2_CELL_HEIGHT = 208;
|
|
7
|
+
export declare const CODEX_V2_DEFAULT_FPS = 8;
|
|
8
|
+
export interface CodexSidecar {
|
|
9
|
+
format: 1;
|
|
10
|
+
adapter: "codex-pet-v2";
|
|
11
|
+
license: string;
|
|
12
|
+
provenance: {
|
|
13
|
+
author: string;
|
|
14
|
+
source: string;
|
|
15
|
+
rights: string;
|
|
16
|
+
};
|
|
17
|
+
reactions?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export interface CodexAtlasInfo {
|
|
20
|
+
fileName: "spritesheet.webp" | "spritesheet.png";
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
mimeType: "image/webp" | "image/png";
|
|
24
|
+
hasAlpha: boolean;
|
|
25
|
+
sha256: string;
|
|
26
|
+
byteLength?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface AdaptCodexOptions {
|
|
29
|
+
pet: unknown;
|
|
30
|
+
sidecar: unknown;
|
|
31
|
+
atlas: CodexAtlasInfo;
|
|
32
|
+
allowSelfHostedPng?: boolean;
|
|
33
|
+
}
|
|
34
|
+
interface CodexStateBase {
|
|
35
|
+
frames: number[];
|
|
36
|
+
loop: boolean;
|
|
37
|
+
}
|
|
38
|
+
export type CodexState = (CodexStateBase & {
|
|
39
|
+
fps: number;
|
|
40
|
+
durations?: never;
|
|
41
|
+
}) | (CodexStateBase & {
|
|
42
|
+
durations: number[];
|
|
43
|
+
fps?: never;
|
|
44
|
+
});
|
|
45
|
+
export interface AdaptedCodexPack {
|
|
46
|
+
name: string;
|
|
47
|
+
displayName: string;
|
|
48
|
+
version: string;
|
|
49
|
+
license: string;
|
|
50
|
+
atlas: {
|
|
51
|
+
src: string;
|
|
52
|
+
sha256: string;
|
|
53
|
+
columns: 8;
|
|
54
|
+
rows: 11;
|
|
55
|
+
cellWidth: 192;
|
|
56
|
+
cellHeight: 208;
|
|
57
|
+
};
|
|
58
|
+
states: Record<string, CodexState>;
|
|
59
|
+
defaultScale: 1;
|
|
60
|
+
directionalStates: Record<"N" | "NE" | "E" | "SE" | "S" | "SW" | "W" | "NW", string>;
|
|
61
|
+
reactionStates: Record<string, string>;
|
|
62
|
+
source: {
|
|
63
|
+
kind: "codex-pet-v2";
|
|
64
|
+
pet: Readonly<Record<string, unknown>>;
|
|
65
|
+
sidecar: CodexSidecar;
|
|
66
|
+
legacyFlat8Fps: boolean;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export declare const CODEX_REACTION_DEFAULTS: Readonly<{
|
|
70
|
+
click: "waving";
|
|
71
|
+
"double-click": "jumping";
|
|
72
|
+
"context-click": "waving";
|
|
73
|
+
scroll: "waiting";
|
|
74
|
+
success: "jumping";
|
|
75
|
+
happy: "waving";
|
|
76
|
+
error: "failed";
|
|
77
|
+
}>;
|
|
78
|
+
export declare function codexLocomotionForDelta(deltaX: number): "running-left" | "running-right";
|
|
79
|
+
export declare function lookStateForAngle(degrees: number): string;
|
|
80
|
+
export declare function adaptCodexPetV2(options: AdaptCodexOptions): AdaptedCodexPack;
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { snapshotPackData } from "@peekling/runtime/pack";
|
|
2
|
+
export const CODEX_V2_WIDTH = 1536;
|
|
3
|
+
export const CODEX_V2_HEIGHT = 2288;
|
|
4
|
+
export const CODEX_V2_COLUMNS = 8;
|
|
5
|
+
export const CODEX_V2_ROWS = 11;
|
|
6
|
+
export const CODEX_V2_CELL_WIDTH = 192;
|
|
7
|
+
export const CODEX_V2_CELL_HEIGHT = 208;
|
|
8
|
+
export const CODEX_V2_DEFAULT_FPS = 8;
|
|
9
|
+
const DEFINITIONS = [
|
|
10
|
+
["idle", 0, 6, [1680, 660, 660, 840, 840, 1920]],
|
|
11
|
+
["running-right", 1, 8, [120, 120, 120, 120, 120, 120, 120, 220]],
|
|
12
|
+
["running-left", 2, 8, [120, 120, 120, 120, 120, 120, 120, 220]],
|
|
13
|
+
["waving", 3, 4, [140, 140, 140, 280]],
|
|
14
|
+
["jumping", 4, 5, [140, 140, 140, 140, 280]],
|
|
15
|
+
["failed", 5, 8, [140, 140, 140, 140, 140, 140, 140, 240]],
|
|
16
|
+
["waiting", 6, 6, [150, 150, 150, 150, 150, 260]],
|
|
17
|
+
["running", 7, 6, [120, 120, 120, 120, 120, 220]],
|
|
18
|
+
["review", 8, 6, [150, 150, 150, 150, 150, 280]],
|
|
19
|
+
];
|
|
20
|
+
const STANDARD_STATES = new Set(DEFINITIONS.map(([name]) => name));
|
|
21
|
+
const SPDX_LICENSE = /^(?:[A-Za-z0-9][A-Za-z0-9.-]{0,63}|DocumentRef-[A-Za-z0-9][A-Za-z0-9.-]{0,31}:LicenseRef-[A-Za-z0-9][A-Za-z0-9.-]{0,31})$/;
|
|
22
|
+
const SEMVER = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
23
|
+
export const CODEX_REACTION_DEFAULTS = Object.freeze({
|
|
24
|
+
click: "waving",
|
|
25
|
+
"double-click": "jumping",
|
|
26
|
+
"context-click": "waving",
|
|
27
|
+
scroll: "waiting",
|
|
28
|
+
success: "jumping",
|
|
29
|
+
happy: "waving",
|
|
30
|
+
error: "failed",
|
|
31
|
+
});
|
|
32
|
+
function object(value) {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
function cleanString(value, field, issues) {
|
|
36
|
+
if (typeof value !== "string" ||
|
|
37
|
+
value.length === 0 ||
|
|
38
|
+
value.length > 512 ||
|
|
39
|
+
/[\u0000-\u001f\u007f]/.test(value)) {
|
|
40
|
+
issues.push(`${field} must be a non-empty data-safe string of at most 512 characters`);
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function validateSidecar(input, issues) {
|
|
46
|
+
if (!object(input)) {
|
|
47
|
+
issues.push("sidecar must be an object");
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
for (const key of Object.keys(input))
|
|
51
|
+
if (!["format", "adapter", "license", "provenance", "reactions"].includes(key))
|
|
52
|
+
issues.push(`sidecar.${key} is not allowed`);
|
|
53
|
+
if (input.format !== 1)
|
|
54
|
+
issues.push("sidecar.format must be 1");
|
|
55
|
+
if (input.adapter !== "codex-pet-v2")
|
|
56
|
+
issues.push('sidecar.adapter must be "codex-pet-v2"');
|
|
57
|
+
const license = cleanString(input.license, "sidecar.license", issues);
|
|
58
|
+
if (license && !SPDX_LICENSE.test(license))
|
|
59
|
+
issues.push("sidecar.license must be an SPDX identifier or LicenseRef");
|
|
60
|
+
if (!object(input.provenance)) {
|
|
61
|
+
issues.push("sidecar.provenance must be an object");
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
for (const key of Object.keys(input.provenance))
|
|
65
|
+
if (!["author", "source", "rights"].includes(key))
|
|
66
|
+
issues.push(`sidecar.provenance.${key} is not allowed`);
|
|
67
|
+
const author = cleanString(input.provenance.author, "sidecar.provenance.author", issues);
|
|
68
|
+
const source = cleanString(input.provenance.source, "sidecar.provenance.source", issues);
|
|
69
|
+
const rights = cleanString(input.provenance.rights, "sidecar.provenance.rights", issues);
|
|
70
|
+
const reactions = {};
|
|
71
|
+
if (input.reactions !== undefined) {
|
|
72
|
+
if (!object(input.reactions) || Object.keys(input.reactions).length > 32) {
|
|
73
|
+
issues.push("sidecar.reactions must be an object with at most 32 entries");
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
for (const [semantic, state] of Object.entries(input.reactions)) {
|
|
77
|
+
if (!/^[a-z][a-z0-9-]{0,63}$/.test(semantic) ||
|
|
78
|
+
typeof state !== "string" ||
|
|
79
|
+
(!STANDARD_STATES.has(state) &&
|
|
80
|
+
!/^look:(?:000|\d{3}(?:\.5)?)$/.test(state))) {
|
|
81
|
+
issues.push(`sidecar.reactions.${semantic} is invalid`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
reactions[semantic] = state;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (!license || !author || !source || !rights)
|
|
90
|
+
return undefined;
|
|
91
|
+
return {
|
|
92
|
+
format: 1,
|
|
93
|
+
adapter: "codex-pet-v2",
|
|
94
|
+
license,
|
|
95
|
+
provenance: { author, source, rights },
|
|
96
|
+
...(Object.keys(reactions).length ? { reactions } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function explicitTiming(pet, name, frames) {
|
|
100
|
+
for (const field of ["animations", "animationDefinitions"]) {
|
|
101
|
+
const container = pet[field];
|
|
102
|
+
if (!object(container) || !object(container[name]))
|
|
103
|
+
continue;
|
|
104
|
+
const durations = container[name].frameDurations;
|
|
105
|
+
if (durations === undefined)
|
|
106
|
+
continue;
|
|
107
|
+
if (!Array.isArray(durations) ||
|
|
108
|
+
durations.length !== frames ||
|
|
109
|
+
durations.some((duration) => !Number.isFinite(duration) ||
|
|
110
|
+
duration < 16 ||
|
|
111
|
+
duration > 10_000) ||
|
|
112
|
+
durations.reduce((sum, duration) => sum + Number(duration), 0) > 60_000) {
|
|
113
|
+
throw new TypeError(`${field}.${name}.frameDurations must match frames and contain 16-10000 ms values totaling at most 60000 ms`);
|
|
114
|
+
}
|
|
115
|
+
{
|
|
116
|
+
return [...durations];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
function hasExplicitTimings(pet) {
|
|
122
|
+
return DEFINITIONS.some(([name, , frames]) => explicitTiming(pet, name, frames) !== undefined);
|
|
123
|
+
}
|
|
124
|
+
export function codexLocomotionForDelta(deltaX) {
|
|
125
|
+
return deltaX < 0 ? "running-left" : "running-right";
|
|
126
|
+
}
|
|
127
|
+
export function lookStateForAngle(degrees) {
|
|
128
|
+
if (!Number.isFinite(degrees))
|
|
129
|
+
throw new TypeError("degrees must be finite");
|
|
130
|
+
const normalized = ((degrees % 360) + 360) % 360;
|
|
131
|
+
const step = Math.round(normalized / 22.5) % 16;
|
|
132
|
+
const value = step * 22.5;
|
|
133
|
+
return `look:${value === 0 ? "000" : Number.isInteger(value) ? String(value).padStart(3, "0") : String(value).padStart(5, "0")}`;
|
|
134
|
+
}
|
|
135
|
+
export function adaptCodexPetV2(options) {
|
|
136
|
+
let checked;
|
|
137
|
+
try {
|
|
138
|
+
const snapshot = snapshotPackData(options);
|
|
139
|
+
if (!object(snapshot))
|
|
140
|
+
throw new TypeError("$ must be a plain data object");
|
|
141
|
+
checked = snapshot;
|
|
142
|
+
}
|
|
143
|
+
catch (cause) {
|
|
144
|
+
throw new TypeError(`Invalid Codex Pet v2 bundle:\n- ${cause instanceof Error ? cause.message : "input cannot be inspected safely"}`);
|
|
145
|
+
}
|
|
146
|
+
for (const key of Object.keys(checked)) {
|
|
147
|
+
if (!["pet", "sidecar", "atlas", "allowSelfHostedPng"].includes(key)) {
|
|
148
|
+
throw new TypeError(`Invalid Codex Pet v2 bundle:\n- options.${key} is not allowed`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const issues = [];
|
|
152
|
+
if (!object(checked.pet))
|
|
153
|
+
throw new TypeError("Invalid Codex Pet v2 bundle:\n- pet.json must be an object");
|
|
154
|
+
const pet = checked.pet;
|
|
155
|
+
const serialized = JSON.stringify(pet);
|
|
156
|
+
if (new TextEncoder().encode(serialized).byteLength > 64 * 1024) {
|
|
157
|
+
issues.push("pet.json exceeds 64 KiB");
|
|
158
|
+
}
|
|
159
|
+
if (pet.spriteVersionNumber !== 2)
|
|
160
|
+
issues.push("spriteVersionNumber must be 2");
|
|
161
|
+
const sidecar = validateSidecar(checked.sidecar, issues);
|
|
162
|
+
if (!object(checked.atlas))
|
|
163
|
+
throw new TypeError("Invalid Codex Pet v2 bundle:\n- atlas must be an object");
|
|
164
|
+
const atlas = checked.atlas;
|
|
165
|
+
for (const key of Object.keys(atlas)) {
|
|
166
|
+
if (![
|
|
167
|
+
"fileName",
|
|
168
|
+
"width",
|
|
169
|
+
"height",
|
|
170
|
+
"mimeType",
|
|
171
|
+
"hasAlpha",
|
|
172
|
+
"sha256",
|
|
173
|
+
"byteLength",
|
|
174
|
+
].includes(key)) {
|
|
175
|
+
issues.push(`atlas.${key} is not allowed`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (checked.allowSelfHostedPng !== undefined &&
|
|
179
|
+
typeof checked.allowSelfHostedPng !== "boolean")
|
|
180
|
+
issues.push("allowSelfHostedPng must be boolean");
|
|
181
|
+
if (atlas.width !== CODEX_V2_WIDTH || atlas.height !== CODEX_V2_HEIGHT) {
|
|
182
|
+
issues.push(`atlas must be exactly ${CODEX_V2_WIDTH}x${CODEX_V2_HEIGHT}`);
|
|
183
|
+
}
|
|
184
|
+
const webp = atlas.fileName === "spritesheet.webp" && atlas.mimeType === "image/webp";
|
|
185
|
+
const png = checked.allowSelfHostedPng === true &&
|
|
186
|
+
atlas.fileName === "spritesheet.png" &&
|
|
187
|
+
atlas.mimeType === "image/png";
|
|
188
|
+
if (!webp && !png)
|
|
189
|
+
issues.push("atlas must be spritesheet.webp (or explicitly allowed self-hosted PNG)");
|
|
190
|
+
if (atlas.hasAlpha !== true)
|
|
191
|
+
issues.push("atlas must include transparency");
|
|
192
|
+
if (typeof atlas.sha256 !== "string" ||
|
|
193
|
+
!/^[a-f0-9]{64}$/.test(atlas.sha256)) {
|
|
194
|
+
issues.push("atlas.sha256 must be 64 lowercase hexadecimal characters");
|
|
195
|
+
}
|
|
196
|
+
if (atlas.byteLength !== undefined &&
|
|
197
|
+
(typeof atlas.byteLength !== "number" ||
|
|
198
|
+
!Number.isSafeInteger(atlas.byteLength) ||
|
|
199
|
+
atlas.byteLength < 1 ||
|
|
200
|
+
atlas.byteLength > 32 * 1024 * 1024)) {
|
|
201
|
+
issues.push("atlas byteLength must be a positive safe integer at most 32 MiB");
|
|
202
|
+
}
|
|
203
|
+
if (issues.length || !sidecar) {
|
|
204
|
+
throw new TypeError(`Invalid Codex Pet v2 bundle:\n- ${issues.join("\n- ")}`);
|
|
205
|
+
}
|
|
206
|
+
const atlasFileName = atlas.fileName;
|
|
207
|
+
const states = {};
|
|
208
|
+
const legacyFlat8Fps = !hasExplicitTimings(pet);
|
|
209
|
+
for (const [name, row, frameCount, defaults] of DEFINITIONS) {
|
|
210
|
+
const frames = Array.from({ length: frameCount }, (_, column) => row * 8 + column);
|
|
211
|
+
const sourceDurations = explicitTiming(pet, name, frameCount);
|
|
212
|
+
states[name] = {
|
|
213
|
+
frames,
|
|
214
|
+
loop: true,
|
|
215
|
+
durations: sourceDurations ?? [...defaults],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
for (let step = 0; step < 16; step++) {
|
|
219
|
+
const degrees = step * 22.5;
|
|
220
|
+
const name = lookStateForAngle(degrees);
|
|
221
|
+
states[name] = {
|
|
222
|
+
frames: [9 * 8 + step],
|
|
223
|
+
fps: CODEX_V2_DEFAULT_FPS,
|
|
224
|
+
loop: true,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
const petName = typeof pet.name === "string" &&
|
|
228
|
+
pet.name.length <= 120 &&
|
|
229
|
+
!/:\/\//.test(pet.name) &&
|
|
230
|
+
/^[\p{L}\p{N}][\p{L}\p{N} ._-]{0,119}$/u.test(pet.name)
|
|
231
|
+
? pet.name
|
|
232
|
+
: "codex-pet";
|
|
233
|
+
const packName = petName
|
|
234
|
+
.toLowerCase()
|
|
235
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
236
|
+
.replace(/^-+|-+$/g, "")
|
|
237
|
+
.slice(0, 64);
|
|
238
|
+
const petVersion = typeof pet.version === "string" && SEMVER.test(pet.version)
|
|
239
|
+
? pet.version
|
|
240
|
+
: "2.0.0";
|
|
241
|
+
const sourcePet = {
|
|
242
|
+
spriteVersionNumber: 2,
|
|
243
|
+
};
|
|
244
|
+
if (petName !== "codex-pet")
|
|
245
|
+
sourcePet.name = petName;
|
|
246
|
+
if (petVersion !== "2.0.0")
|
|
247
|
+
sourcePet.version = petVersion;
|
|
248
|
+
return {
|
|
249
|
+
name: /^[a-z][a-z0-9-]{0,63}$/.test(packName) ? packName : "codex-pet",
|
|
250
|
+
displayName: petName,
|
|
251
|
+
version: petVersion,
|
|
252
|
+
license: sidecar.license,
|
|
253
|
+
atlas: {
|
|
254
|
+
src: atlasFileName,
|
|
255
|
+
sha256: atlas.sha256,
|
|
256
|
+
columns: 8,
|
|
257
|
+
rows: 11,
|
|
258
|
+
cellWidth: 192,
|
|
259
|
+
cellHeight: 208,
|
|
260
|
+
},
|
|
261
|
+
states,
|
|
262
|
+
defaultScale: 1,
|
|
263
|
+
directionalStates: {
|
|
264
|
+
N: "running-right",
|
|
265
|
+
NE: "running-right",
|
|
266
|
+
E: "running-right",
|
|
267
|
+
SE: "running-right",
|
|
268
|
+
S: "running-right",
|
|
269
|
+
SW: "running-left",
|
|
270
|
+
W: "running-left",
|
|
271
|
+
NW: "running-left",
|
|
272
|
+
},
|
|
273
|
+
reactionStates: { ...CODEX_REACTION_DEFAULTS, ...sidecar.reactions },
|
|
274
|
+
source: {
|
|
275
|
+
kind: "codex-pet-v2",
|
|
276
|
+
pet: Object.freeze(sourcePet),
|
|
277
|
+
sidecar,
|
|
278
|
+
legacyFlat8Fps,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@peekling/adapter-codex-pet",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Optional data-only Codex Pet v2 compatibility adapter for Peekling",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Prajwal S. Venkateshmurthy",
|
|
8
|
+
"url": "https://prajwal.me"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/peekling/peekling-engine.git"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/peekling/peekling-engine#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/peekling/peekling-engine/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=22.14.0"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"provenance": true
|
|
25
|
+
},
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/**/*.js",
|
|
29
|
+
"dist/**/*.d.ts",
|
|
30
|
+
"dist/**/*.d.ts.map",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"NOTICE",
|
|
34
|
+
"AUTHORS",
|
|
35
|
+
"LICENSING.md"
|
|
36
|
+
],
|
|
37
|
+
"main": "./dist/index.js",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@peekling/runtime": "0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsc -b",
|
|
50
|
+
"prepack": "npm --prefix ../.. run build",
|
|
51
|
+
"test": "npm run build && node --test --experimental-strip-types test/*.test.ts",
|
|
52
|
+
"typecheck": "tsc -b --pretty false"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"typescript": "^7.0.2"
|
|
56
|
+
}
|
|
57
|
+
}
|