@s8fy/pptx-parser 1.0.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 +36 -0
- package/README.md +336 -0
- package/dist/index.cjs +24649 -0
- package/dist/index.d.cts +1212 -0
- package/dist/index.d.ts +1212 -0
- package/dist/index.js +24642 -0
- package/dist/index.umd.js +24653 -0
- package/dist/js-worker.js +23220 -0
- package/dist/mbt/main.wasm +0 -0
- package/dist/mbt/pdf-converter.wasm +0 -0
- package/dist/pdf-worker.js +1692 -0
- package/dist/wasm-worker.js +1137 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Default license for this package's original code and modifications:
|
|
2
|
+
|
|
3
|
+
PolyForm Noncommercial License 1.0.0
|
|
4
|
+
https://polyformproject.org/licenses/noncommercial/1.0.0
|
|
5
|
+
|
|
6
|
+
Required Notice: Copyright (c) 2025-PRESENT hustcer
|
|
7
|
+
|
|
8
|
+
This package is source-available, not open source. No commercial use is granted
|
|
9
|
+
to third parties for the licensor's original code and modifications unless
|
|
10
|
+
separately licensed.
|
|
11
|
+
|
|
12
|
+
Third-party notice:
|
|
13
|
+
This package includes material derived from `pptxtojson` by `pipipi-pikachu`.
|
|
14
|
+
The preserved upstream MIT notice for those portions is reproduced below.
|
|
15
|
+
|
|
16
|
+
MIT License
|
|
17
|
+
|
|
18
|
+
Copyright (c) 2020-PRESENT pipipi-pikachu
|
|
19
|
+
|
|
20
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
21
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
22
|
+
in the Software without restriction, including without limitation the rights
|
|
23
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
24
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
25
|
+
furnished to do so, subject to the following conditions:
|
|
26
|
+
|
|
27
|
+
The above copyright notice and this permission notice shall be included in all
|
|
28
|
+
copies or substantial portions of the Software.
|
|
29
|
+
|
|
30
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
31
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
32
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
33
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
34
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
35
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
36
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# @s8fy/pptx-parser
|
|
2
|
+
|
|
3
|
+
A browser-first TypeScript library for parsing `.pptx` files into structured data with JavaScript or WebAssembly, and converting original PPTX bytes to PDF. Node.js **22.12+** is also supported through ESM and CommonJS entrypoints.
|
|
4
|
+
|
|
5
|
+
### Architecture
|
|
6
|
+
|
|
7
|
+
- **Structured text output** — text content outputs `TextParagraph[]` JSON arrays with full formatting metadata, replacing raw HTML strings and eliminating `dangerouslySetInnerHTML`
|
|
8
|
+
- **Pure data output** — parser only outputs raw parsed data; model adaptation lives in `@s8fy/pptx-core` and DOM/SVG rendering lives in `@s8fy/pptx-renderer`
|
|
9
|
+
- **Full TypeScript rewrite** — all source files converted from JavaScript to TypeScript with strict mode
|
|
10
|
+
|
|
11
|
+
### New Features
|
|
12
|
+
|
|
13
|
+
- **Embedded font extraction** — extracts font binary data (EOT/ODTTF/TTF/OTF/WOFF) from PPTX embedded font list; opt-in via `extractFonts` option (default `false` to avoid loading large CJK fonts)
|
|
14
|
+
- **Arrow endpoints** — parses `a:headEnd`/`a:tailEnd` on lines with support for arrow, stealth, diamond, dot marker types and sm/med/lg sizing
|
|
15
|
+
- **Chart type mapping** — OOXML chart types mapped to simplified types (`bar`, `column`, `line`, `pie`, `ring`, `area`, `radar`, `scatter`) directly in the parser
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm add @s8fy/pptx-parser
|
|
21
|
+
# or
|
|
22
|
+
npm install @s8fy/pptx-parser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Browser
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<input
|
|
31
|
+
type="file"
|
|
32
|
+
accept="application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
33
|
+
/>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
import { parsePptx, revokeBlobUrls } from '@s8fy/pptx-parser'
|
|
38
|
+
|
|
39
|
+
const input = document.querySelector('input[type="file"]')
|
|
40
|
+
if (!(input instanceof HTMLInputElement)) throw new Error('Missing file input')
|
|
41
|
+
input.addEventListener('change', async () => {
|
|
42
|
+
const file = input.files?.[0]
|
|
43
|
+
if (!file) return
|
|
44
|
+
try {
|
|
45
|
+
const result = await parsePptx(await file.arrayBuffer(), { parser: 'wasm' })
|
|
46
|
+
console.log(result)
|
|
47
|
+
// Keep result while its media is used; see cleanup below.
|
|
48
|
+
revokeBlobUrls(result)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(error)
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Node.js
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const { parsePptx } = require('@s8fy/pptx-parser')
|
|
59
|
+
const { readFileSync } = require('node:fs')
|
|
60
|
+
|
|
61
|
+
async function main() {
|
|
62
|
+
const bytes = readFileSync('test.pptx')
|
|
63
|
+
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)
|
|
64
|
+
const result = await parsePptx(buffer)
|
|
65
|
+
console.log(result)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
main().catch(console.error)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## API
|
|
72
|
+
|
|
73
|
+
### `parsePptx(file: ArrayBuffer, options?: ParseOptions): Promise<ParseResult>`
|
|
74
|
+
|
|
75
|
+
#### Options
|
|
76
|
+
|
|
77
|
+
| Option | Type | Default | Description |
|
|
78
|
+
| -------------- | ---------------- | ---------------- | --------------------------------------------------------------- |
|
|
79
|
+
| `extractFonts` | `boolean` | `false` | Whether to extract embedded font binary data |
|
|
80
|
+
| `parser` | `'js' \| 'wasm'` | `'js'` | Parser engine for this low-level package |
|
|
81
|
+
| `unzipMode` | `'js' \| 'wasm'` | `'wasm'` | ZIP engine when `parser: 'wasm'` |
|
|
82
|
+
| `wasmUrl` | `string \| URL` | Package-relative | Override the parser WASM URL for CDN or self-hosted deployments |
|
|
83
|
+
| `timing` | `boolean` | `false` | Include per-phase timing in the optional `_timing` result field |
|
|
84
|
+
| `signal` | `AbortSignal` | None | Cancel this parse request without cancelling other callers |
|
|
85
|
+
|
|
86
|
+
`@s8fy/pptx-core`, the renderer, and the viewer facade default to the WASM parser. The low-level parser defaults to JavaScript for backward compatibility.
|
|
87
|
+
|
|
88
|
+
The package ships package-relative JS/WASM/PDF workers and both WASM binaries. Modern bundlers such as Vite copy these assets from the static runtime references. The UMD entry resolves adjacent assets from the current `<script>` URL, so keep the published `dist/` files together when loading it directly. CommonJS is intended for Node.js/SSR and can load the packaged WASM files directly when `parser: 'wasm'` is requested. Browser applications should prefer the ESM entry. Both the parser and PDF APIs accept explicit `wasmUrl` overrides; the PDF API additionally accepts `workerUrl`.
|
|
89
|
+
|
|
90
|
+
### Parser WASM deployment
|
|
91
|
+
|
|
92
|
+
Automatic package-relative resolution is the default. For a CDN, offline bundle, or private deployment, copy `node_modules/@s8fy/pptx-parser/dist/mbt/main.wasm` into the application's static assets and pass its deployed URL:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
await parsePptx(buffer, {
|
|
96
|
+
parser: 'wasm',
|
|
97
|
+
wasmUrl: '/vendor/s8fy/main.wasm',
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- The package-relative Worker/WASM paths are covered by the repository's Vite consumer smoke. For other bundlers, verify the deployed assets; use the bundler's public/copy-asset facility when supplying an override.
|
|
102
|
+
- Next.js and Nuxt serve the copied file from `public/`; call the SDK from client-side code and pass a root-relative URL.
|
|
103
|
+
- SvelteKit serves it from `static/`; pass the matching root-relative URL.
|
|
104
|
+
- Angular CLI can add the source file as an `assets` glob in `angular.json`, then pass the configured output URL.
|
|
105
|
+
|
|
106
|
+
The same-origin server should return the file without HTML fallback content and preferably with `Content-Type: application/wasm`. Cross-origin URLs must allow the application origin through CORS.
|
|
107
|
+
|
|
108
|
+
### PDF and resource helpers
|
|
109
|
+
|
|
110
|
+
- `pptxToPdf(file, options?)` returns PDF bytes as `Uint8Array`.
|
|
111
|
+
- `pptxToPdfBlob(file, options?)` returns a browser `Blob`.
|
|
112
|
+
- `revokeBlobUrls(data)` releases Blob URLs created while parsing media.
|
|
113
|
+
- `transferBlobUrls(source, target)` moves cleanup ownership to a custom adapted object, without copying bytes. After transfer, clean up `target` instead of `source`.
|
|
114
|
+
- `ResourcePolicyError` and `RESOURCE_POLICY_CONTRACT` expose the parser's deterministic input/resource-limit contract.
|
|
115
|
+
|
|
116
|
+
PDF conversion accepts explicit `wasmUrl`, `wasmBytes`, and `workerUrl` overrides, font/emoji fallback sources, watermarks, and signed-license runtime data. Prefer the higher-level `@s8fy/pptx-core` facade unless the raw parser result is required.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { pptxToPdf } from '@s8fy/pptx-parser'
|
|
120
|
+
|
|
121
|
+
export async function convertFile(file: File): Promise<Uint8Array> {
|
|
122
|
+
return pptxToPdf(await file.arrayBuffer(), {
|
|
123
|
+
unicodeFallbackFont: { url: '/fonts/NotoSansSC-Regular.ttf' },
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Serve that font URL yourself or omit it for documents that do not need a Unicode fallback. The PDF API always uses its PDF WASM engine; choosing JS for parsing does not make PDF conversion JavaScript-only. PDF `wasmUrl` points to `pdf-converter.wasm`, not parser `main.wasm`. Browser-window conversion requires a working PDF Worker and does not silently retry on the main thread if the Worker fails. Node conversion runs on the current thread.
|
|
129
|
+
|
|
130
|
+
Both PDF functions accept `signal` for cancellation. Font options include `regularFonts` (with regular/bold/italic/boldItalic styles), `unicodeFallbackFont`, `mathFallbackFont`, `emojiFallbackFont`, and `emojiBitmapFallback`. Browser font CSS does not automatically supply PDF font bytes. The low-level `watermarks`/`licenseRuntime` options differ from core/viewer's `watermark`/`license` facade; do not interchange their option objects.
|
|
131
|
+
|
|
132
|
+
### Cancellation, errors, and cleanup
|
|
133
|
+
|
|
134
|
+
Pass `new AbortController().signal` to parsing or PDF conversion, then call that controller's `abort()` when the request is obsolete. Cancellation rejects the request; synchronous work on the same JavaScript thread cannot be interrupted until control returns.
|
|
135
|
+
|
|
136
|
+
When a parsed result is no longer displayed or otherwise used, call `revokeBlobUrls(result)` on that same object. Media URLs may be `blob:` URLs rather than base64 strings and are not portable across sessions. A JSON clone does not retain the original cleanup ownership. Use `transferBlobUrls` only when intentionally replacing the owner with an adapted model.
|
|
137
|
+
|
|
138
|
+
`ResourcePolicyError.details` contains `reason`, `stage`, `subject`, `limit`, `observed`, `unit`, and `policyVersion`. These engineering limits are independent of commercial licenses (`commercialUpgradeApplicable` is false); catch and inspect the error rather than retrying the same oversized input. Other parse/conversion failures can be ordinary `Error` instances.
|
|
139
|
+
|
|
140
|
+
#### ParseResult
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
{
|
|
144
|
+
slides: Slide[]
|
|
145
|
+
themeColors: string[]
|
|
146
|
+
hlinkColor: string | null
|
|
147
|
+
fonts: EmbeddedFont[]
|
|
148
|
+
size: {
|
|
149
|
+
width: number // px
|
|
150
|
+
height: number // px
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Output Example
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
{
|
|
159
|
+
slides: [
|
|
160
|
+
{
|
|
161
|
+
fill: { type: 'color', value: '#FFFFFF' },
|
|
162
|
+
elements: [
|
|
163
|
+
{
|
|
164
|
+
type: 'text',
|
|
165
|
+
left: 100,
|
|
166
|
+
top: 50,
|
|
167
|
+
width: 600,
|
|
168
|
+
height: 80,
|
|
169
|
+
content: [
|
|
170
|
+
{
|
|
171
|
+
align: 'center',
|
|
172
|
+
lineHeight: 1.2,
|
|
173
|
+
runs: [
|
|
174
|
+
{
|
|
175
|
+
text: 'Hello World',
|
|
176
|
+
fontSize: '24pt',
|
|
177
|
+
fontFamily: 'Calibri',
|
|
178
|
+
fontWeight: 'bold',
|
|
179
|
+
color: '#333333'
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
],
|
|
184
|
+
vAlign: 'mid',
|
|
185
|
+
name: 'Title 1',
|
|
186
|
+
order: 0,
|
|
187
|
+
// ...
|
|
188
|
+
},
|
|
189
|
+
// more elements...
|
|
190
|
+
],
|
|
191
|
+
layoutElements: [/* master/layout elements */],
|
|
192
|
+
note: 'Speaker notes...',
|
|
193
|
+
transition: { type: 'fade', duration: 500, direction: null }
|
|
194
|
+
},
|
|
195
|
+
// more slides...
|
|
196
|
+
],
|
|
197
|
+
themeColors: ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#5B9BD5', '#70AD47'],
|
|
198
|
+
hlinkColor: null,
|
|
199
|
+
fonts: [/* embedded fonts if extractFonts: true */],
|
|
200
|
+
size: { width: 960, height: 540 }
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Element Types
|
|
205
|
+
|
|
206
|
+
### Text (`type: 'text'`)
|
|
207
|
+
|
|
208
|
+
Text box with structured paragraphs. `content` is a `TextParagraph[]` array containing runs with font/color/decoration info, list/bullet info, and paragraph-level alignment/spacing.
|
|
209
|
+
|
|
210
|
+
### Shape (`type: 'shape'`)
|
|
211
|
+
|
|
212
|
+
Predefined or custom shapes. Also uses `TextParagraph[]` for `content`. Includes `shapType`, optional `path` (SVG), `keypoints`, and arrow endpoints (`headEnd`/`tailEnd`).
|
|
213
|
+
|
|
214
|
+
### Image (`type: 'image'`)
|
|
215
|
+
|
|
216
|
+
Image element with `src` (data URI or an owned Blob URL), optional `rect` (crop), `geom` (clip shape), and `filters` (sharpen, brightness, contrast, saturation, color temperature).
|
|
217
|
+
|
|
218
|
+
### Table (`type: 'table'`)
|
|
219
|
+
|
|
220
|
+
Table with `data` (2D `TableCell[][]`), `rowHeights`, `colWidths`. Cells support `rowSpan`/`colSpan` merging, per-cell borders and fill.
|
|
221
|
+
|
|
222
|
+
### Chart (`type: 'chart'`)
|
|
223
|
+
|
|
224
|
+
Chart data with `chartType` (`bar`, `column`, `line`, `pie`, `ring`, `area`, `radar`, `scatter`), `rawChartType` (original OOXML type), `colors`, and optional `barDir`, `marker`, `holeSize`, `grouping`, `style`.
|
|
225
|
+
|
|
226
|
+
### Video (`type: 'video'`)
|
|
227
|
+
|
|
228
|
+
Video element with `blob` (Blob URL) or `src` (external URL), optional `poster` (thumbnail image from slide), `ext` (file extension), and `rotate`.
|
|
229
|
+
|
|
230
|
+
### Audio (`type: 'audio'`)
|
|
231
|
+
|
|
232
|
+
Audio element with `blob` (Blob URL, optional if format unsupported), optional `ext` (file extension), and `rotate`.
|
|
233
|
+
|
|
234
|
+
### Diagram (`type: 'diagram'`)
|
|
235
|
+
|
|
236
|
+
SmartArt with `elements` (Shape/Text sub-elements) and `textList` (fallback text content).
|
|
237
|
+
|
|
238
|
+
### Math (`type: 'math'`)
|
|
239
|
+
|
|
240
|
+
Math formula with `latex` (LaTeX expression), `picBase64` (fallback image), and optional `text` (mixed text/formula content).
|
|
241
|
+
|
|
242
|
+
### Group (`type: 'group'`)
|
|
243
|
+
|
|
244
|
+
Group container with nested `elements` array.
|
|
245
|
+
|
|
246
|
+
## Structured Text Format
|
|
247
|
+
|
|
248
|
+
Text content uses structured `TextParagraph[]` instead of HTML strings:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
interface TextParagraph {
|
|
252
|
+
align: string // 'left' | 'center' | 'right' | 'justify'
|
|
253
|
+
lineHeight?: number // CSS line-height value
|
|
254
|
+
spaceBefore?: string // e.g. '12pt'
|
|
255
|
+
spaceAfter?: string // e.g. '6pt'
|
|
256
|
+
list?: {
|
|
257
|
+
type: 'ul' | 'ol'
|
|
258
|
+
level: number
|
|
259
|
+
bullet?: BulletInfo
|
|
260
|
+
}
|
|
261
|
+
runs: (TextRun | TextLink | TextBreak)[]
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface TextRun {
|
|
265
|
+
text: string
|
|
266
|
+
color?: string | GradientColor
|
|
267
|
+
fontSize?: string // e.g. '18pt'
|
|
268
|
+
fontFamily?: string
|
|
269
|
+
fontWeight?: string // e.g. 'bold'
|
|
270
|
+
fontStyle?: string // e.g. 'italic'
|
|
271
|
+
textDecoration?: string // e.g. 'underline'
|
|
272
|
+
textDecorationLine?: string // e.g. 'line-through'
|
|
273
|
+
letterSpacing?: string
|
|
274
|
+
verticalAlign?: string // 'super' | 'sub'
|
|
275
|
+
textShadow?: string
|
|
276
|
+
highlightColor?: string
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
interface TextLink extends TextRun {
|
|
280
|
+
linkURL: string
|
|
281
|
+
linkColor?: string
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
interface TextBreak {
|
|
285
|
+
type: 'br'
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Fill Types
|
|
290
|
+
|
|
291
|
+
All fill-capable elements support four fill types:
|
|
292
|
+
|
|
293
|
+
- **Color** (`type: 'color'`) — solid color value
|
|
294
|
+
- **Image** (`type: 'image'`) — base64 image with opacity
|
|
295
|
+
- **Gradient** (`type: 'gradient'`) — linear/circle/rect/shape gradient with color stops
|
|
296
|
+
- **Pattern** (`type: 'pattern'`) — pattern type with foreground/background colors
|
|
297
|
+
|
|
298
|
+
## Embedded Fonts
|
|
299
|
+
|
|
300
|
+
When `extractFonts: true` is passed:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
interface EmbeddedFont {
|
|
304
|
+
typeface: string
|
|
305
|
+
styles: {
|
|
306
|
+
regular?: { path: string; data: ArrayBuffer }
|
|
307
|
+
bold?: { path: string; data: ArrayBuffer }
|
|
308
|
+
italic?: { path: string; data: ArrayBuffer }
|
|
309
|
+
boldItalic?: { path: string; data: ArrayBuffer }
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Build Outputs
|
|
315
|
+
|
|
316
|
+
| Format | File | Usage |
|
|
317
|
+
| ------ | ------------------- | --------------------------------------------- |
|
|
318
|
+
| ESM | `dist/index.js` | Modern bundlers (Vite, Webpack 5+) |
|
|
319
|
+
| CJS | `dist/index.cjs` | Node.js `require()` |
|
|
320
|
+
| UMD | `dist/index.umd.js` | Browser `<script>` tag (global: `pptxParser`) |
|
|
321
|
+
|
|
322
|
+
## Type Definitions
|
|
323
|
+
|
|
324
|
+
The package root exports the complete bundled TypeScript declarations through its `types` and conditional `exports` fields. No deep import is required.
|
|
325
|
+
|
|
326
|
+
WASM parsing/PDF conversion requires WebAssembly GC support. Workers may be unavailable in some hosts: parsing has a main-thread fallback, while browser-window PDF conversion requires its Worker. The parsed model is a subset of PowerPoint behavior; output presence does not imply pixel-identical rendering or support for every animation/chart effect.
|
|
327
|
+
|
|
328
|
+
## Credits
|
|
329
|
+
|
|
330
|
+
This library is based on [pptxtojson](https://github.com/pipipi-pikachu/pptxtojson) by pipipi-pikachu.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
Default license for original contributions: `PolyForm-Noncommercial-1.0.0`
|
|
335
|
+
|
|
336
|
+
Commercial use of those contributions requires a separate commercial license.
|