ddex-json-codec 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/LICENSE.md +21 -0
- package/README.md +105 -0
- package/dist/index.d.mts +290 -0
- package/dist/index.mjs +1209 -0
- package/package.json +54 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kazhs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# ddex-json-codec
|
|
2
|
+
|
|
3
|
+
DDEX ERN規格のXML/JSON相互変換ライブラリ(TypeScript)
|
|
4
|
+
|
|
5
|
+
## 特徴
|
|
6
|
+
|
|
7
|
+
- ERN XMLからTypeScript型付きオブジェクトへの変換、およびその逆変換
|
|
8
|
+
- ERN 3.8系 / 4系に対応(4系は未実装)
|
|
9
|
+
- バージョン自動検出
|
|
10
|
+
- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) ベース
|
|
11
|
+
- Node.js >= 20
|
|
12
|
+
|
|
13
|
+
## インストール
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install ddex-json-codec
|
|
17
|
+
# or
|
|
18
|
+
pnpm add ddex-json-codec
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 使い方
|
|
22
|
+
|
|
23
|
+
### XML → JSON
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { xmlToJson } from 'ddex-json-codec';
|
|
27
|
+
|
|
28
|
+
const message = xmlToJson(xmlString);
|
|
29
|
+
console.log(message.ernVersion); // "3.8.1"
|
|
30
|
+
console.log(message.resourceList); // SoundRecording[]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### JSON → XML
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { jsonToXml } from 'ddex-json-codec';
|
|
37
|
+
|
|
38
|
+
const xml = jsonToXml(message);
|
|
39
|
+
// バージョン指定も可
|
|
40
|
+
const xml382 = jsonToXml(message, '3.8.2');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### バージョン検出
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { detectVersion } from 'ddex-json-codec';
|
|
47
|
+
|
|
48
|
+
const version = detectVersion(xmlString); // "3.8.1"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 対応バージョン
|
|
52
|
+
|
|
53
|
+
| ERN バージョン | 状態 |
|
|
54
|
+
|---|---|
|
|
55
|
+
| 3.8 | 実装済み |
|
|
56
|
+
| 3.8.1 | 実装済み |
|
|
57
|
+
| 3.8.2 | 実装済み |
|
|
58
|
+
| 3.8.3 | 実装済み |
|
|
59
|
+
| 4.1 | 未実装 |
|
|
60
|
+
| 4.1.1 | 未実装 |
|
|
61
|
+
| 4.2 | 未実装 |
|
|
62
|
+
| 4.3 | 未実装 |
|
|
63
|
+
| 4.3.1 | 未実装 |
|
|
64
|
+
| 4.3.2 | 未実装 |
|
|
65
|
+
|
|
66
|
+
## API リファレンス
|
|
67
|
+
|
|
68
|
+
### `xmlToJson(xml: string): DdexMessage`
|
|
69
|
+
|
|
70
|
+
ERN XMLを解析し、型付きオブジェクトを返す。バージョンは自動検出される。
|
|
71
|
+
|
|
72
|
+
### `jsonToXml(message: DdexMessage, version?: ErnVersion): string`
|
|
73
|
+
|
|
74
|
+
`DdexMessage` をERN XMLに変換する。`version` 省略時は `message.ernVersion` を使用。
|
|
75
|
+
|
|
76
|
+
### `detectVersion(xml: string): ErnVersion`
|
|
77
|
+
|
|
78
|
+
ERN XMLのnamespace URIからバージョンを検出して返す。
|
|
79
|
+
|
|
80
|
+
### 型
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface DdexMessage {
|
|
84
|
+
ernVersion: ErnVersion;
|
|
85
|
+
messageHeader: MessageHeader;
|
|
86
|
+
updateIndicator?: string;
|
|
87
|
+
resourceList: SoundRecording[];
|
|
88
|
+
releaseList: Release[];
|
|
89
|
+
dealList: ReleaseDeal[];
|
|
90
|
+
partyList?: Party[]; // 4系のみ
|
|
91
|
+
trackReleaseList?: TrackRelease[]; // 4系のみ
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
エクスポートされる型一覧:
|
|
96
|
+
|
|
97
|
+
`DdexMessage`, `ErnVersion`, `ErnMajorVersion`, `MessageHeader`, `MessageParty`,
|
|
98
|
+
`SoundRecording`, `Release`, `TrackRelease`, `ResourceGroup`, `ReleaseResourceReference`,
|
|
99
|
+
`ReleaseDeal`, `Deal`, `DealTerms`, `Usage`,
|
|
100
|
+
`Party`, `Artist`, `DisplayArtist`, `ResourceContributor`, `IndirectResourceContributor`,
|
|
101
|
+
`TextWithAttribute`, `Genre`, `PLine`, `CLine`, `Title`
|
|
102
|
+
|
|
103
|
+
## ライセンス
|
|
104
|
+
|
|
105
|
+
[MIT](./LICENSE.md)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
//#region src/types/common.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* XML要素が属性とテキストを両方持つ場合の汎用型
|
|
4
|
+
*
|
|
5
|
+
* 例: <TitleText languageAndScriptCode="en">Some Song</TitleText>
|
|
6
|
+
* → { value: "Some Song", languageAndScriptCode: "en" }
|
|
7
|
+
*/
|
|
8
|
+
type TextWithAttribute<T extends Record<string, string> = Record<string, never>> = {
|
|
9
|
+
value: string;
|
|
10
|
+
} & T;
|
|
11
|
+
interface Title {
|
|
12
|
+
titleText: string;
|
|
13
|
+
subTitle?: string;
|
|
14
|
+
titleType?: string;
|
|
15
|
+
}
|
|
16
|
+
interface PLine {
|
|
17
|
+
year?: string;
|
|
18
|
+
pLineText: string;
|
|
19
|
+
}
|
|
20
|
+
interface CLine {
|
|
21
|
+
year?: string;
|
|
22
|
+
cLineText: string;
|
|
23
|
+
}
|
|
24
|
+
interface Genre {
|
|
25
|
+
genreText: string;
|
|
26
|
+
subGenre?: string;
|
|
27
|
+
}
|
|
28
|
+
/** 4系: DisplayTitle(territory+lang属性付き) */
|
|
29
|
+
interface DisplayTitle {
|
|
30
|
+
titleText: string;
|
|
31
|
+
subTitle?: string;
|
|
32
|
+
applicableTerritoryCode?: string;
|
|
33
|
+
languageAndScriptCode?: string;
|
|
34
|
+
isDefault?: boolean;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/types/party.d.ts
|
|
38
|
+
interface Party {
|
|
39
|
+
partyReference: string;
|
|
40
|
+
partyId?: string[];
|
|
41
|
+
partyName?: PartyName[];
|
|
42
|
+
}
|
|
43
|
+
interface PartyName {
|
|
44
|
+
fullName: string;
|
|
45
|
+
fullNameIndexed?: string;
|
|
46
|
+
languageAndScriptCode?: string;
|
|
47
|
+
}
|
|
48
|
+
interface Artist {
|
|
49
|
+
/** 解決済みの名前(3.8系: FullNameから直接取得、4系: PartyListから解決) */
|
|
50
|
+
name: string;
|
|
51
|
+
/** 4系のみ: 元の参照ID(ラウンドトリップ用) */
|
|
52
|
+
partyReference?: string;
|
|
53
|
+
partyId?: string[];
|
|
54
|
+
roles?: string[];
|
|
55
|
+
}
|
|
56
|
+
interface DisplayArtist {
|
|
57
|
+
artist: Artist;
|
|
58
|
+
sequenceNumber?: number;
|
|
59
|
+
}
|
|
60
|
+
interface ResourceContributor {
|
|
61
|
+
name: string;
|
|
62
|
+
role: string;
|
|
63
|
+
sequenceNumber?: number;
|
|
64
|
+
/** UserDefined時の属性 */
|
|
65
|
+
roleNamespace?: string;
|
|
66
|
+
roleUserDefinedValue?: string;
|
|
67
|
+
}
|
|
68
|
+
interface IndirectResourceContributor {
|
|
69
|
+
name: string;
|
|
70
|
+
role: string;
|
|
71
|
+
sequenceNumber?: number;
|
|
72
|
+
}
|
|
73
|
+
/** 4系: PartyReference参照のContributor */
|
|
74
|
+
interface Contributor {
|
|
75
|
+
contributorPartyReference: string;
|
|
76
|
+
/** PartyList解決後に埋める */
|
|
77
|
+
name?: string;
|
|
78
|
+
role: string;
|
|
79
|
+
sequenceNumber?: number;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/types/sound-recording.d.ts
|
|
83
|
+
interface SoundRecording {
|
|
84
|
+
resourceReference: string;
|
|
85
|
+
type?: string;
|
|
86
|
+
soundRecordingId?: SoundRecordingId;
|
|
87
|
+
referenceTitle?: ReferenceTitle$1;
|
|
88
|
+
displayArtists: DisplayArtist[];
|
|
89
|
+
duration?: string;
|
|
90
|
+
creationDate?: string;
|
|
91
|
+
languageOfPerformance?: string;
|
|
92
|
+
pLine?: PLine;
|
|
93
|
+
/** 3.8系: territory別の詳細 */
|
|
94
|
+
detailsByTerritory?: SoundRecordingDetailsByTerritory[];
|
|
95
|
+
/** 4系: フラットなタイトルテキスト */
|
|
96
|
+
displayTitleText?: string;
|
|
97
|
+
/** 4系: 複数のDisplayTitle(territory+lang属性) */
|
|
98
|
+
displayTitles?: DisplayTitle[];
|
|
99
|
+
/** 4系: PartyReference参照のContributor */
|
|
100
|
+
contributors?: Contributor[];
|
|
101
|
+
}
|
|
102
|
+
interface SoundRecordingId {
|
|
103
|
+
isrc?: string;
|
|
104
|
+
catalogNumber?: string;
|
|
105
|
+
}
|
|
106
|
+
interface ReferenceTitle$1 {
|
|
107
|
+
titleText: string;
|
|
108
|
+
subTitle?: string;
|
|
109
|
+
}
|
|
110
|
+
interface SoundRecordingDetailsByTerritory {
|
|
111
|
+
territoryCode: string[];
|
|
112
|
+
displayArtists?: DisplayArtist[];
|
|
113
|
+
titles?: Title[];
|
|
114
|
+
labelName?: string;
|
|
115
|
+
pLine?: PLine;
|
|
116
|
+
genre?: Genre;
|
|
117
|
+
parentalWarningType?: string;
|
|
118
|
+
sequenceNumber?: number;
|
|
119
|
+
resourceContributors?: ResourceContributor[];
|
|
120
|
+
indirectResourceContributors?: IndirectResourceContributor[];
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/types/release.d.ts
|
|
124
|
+
interface Release {
|
|
125
|
+
releaseReference: string;
|
|
126
|
+
releaseType?: string;
|
|
127
|
+
releaseId?: ReleaseId;
|
|
128
|
+
referenceTitle?: ReferenceTitle;
|
|
129
|
+
displayArtists: DisplayArtist[];
|
|
130
|
+
releaseResourceReferences?: ReleaseResourceReference[];
|
|
131
|
+
resourceGroup?: ResourceGroup;
|
|
132
|
+
duration?: string;
|
|
133
|
+
pLine?: PLine;
|
|
134
|
+
cLine?: CLine;
|
|
135
|
+
/** 3.8系: territory別の詳細 */
|
|
136
|
+
detailsByTerritory?: ReleaseDetailsByTerritory[];
|
|
137
|
+
/** 4系: フラットなタイトルテキスト */
|
|
138
|
+
displayTitleText?: string;
|
|
139
|
+
/** 4系: 複数のDisplayTitle(territory+lang属性) */
|
|
140
|
+
displayTitles?: DisplayTitle[];
|
|
141
|
+
/** 4系: ReleaseLabelReference(PartyRef値) */
|
|
142
|
+
releaseLabelReferences?: string[];
|
|
143
|
+
genre?: Genre;
|
|
144
|
+
parentalWarningType?: string;
|
|
145
|
+
}
|
|
146
|
+
interface ReferenceTitle {
|
|
147
|
+
titleText: string;
|
|
148
|
+
subTitle?: string;
|
|
149
|
+
}
|
|
150
|
+
interface ReleaseId {
|
|
151
|
+
icpn?: string;
|
|
152
|
+
isEan?: boolean;
|
|
153
|
+
isrc?: string;
|
|
154
|
+
gridOrIcpn?: string;
|
|
155
|
+
catalogNumber?: string;
|
|
156
|
+
proprietaryId?: string;
|
|
157
|
+
}
|
|
158
|
+
interface ReleaseResourceReference {
|
|
159
|
+
value: string;
|
|
160
|
+
releaseResourceType?: string;
|
|
161
|
+
}
|
|
162
|
+
interface ResourceGroup {
|
|
163
|
+
sequenceNumber?: number;
|
|
164
|
+
title?: string;
|
|
165
|
+
resourceGroups?: ResourceGroup[];
|
|
166
|
+
resourceGroupContentItems?: ResourceGroupContentItem[];
|
|
167
|
+
}
|
|
168
|
+
interface ResourceGroupContentItem {
|
|
169
|
+
sequenceNumber?: number;
|
|
170
|
+
resourceType?: string;
|
|
171
|
+
releaseResourceReference: ReleaseResourceReference;
|
|
172
|
+
}
|
|
173
|
+
interface TrackRelease {
|
|
174
|
+
releaseReference: string;
|
|
175
|
+
releaseId?: ReleaseId;
|
|
176
|
+
referenceTitle?: ReferenceTitle;
|
|
177
|
+
displayArtists?: DisplayArtist[];
|
|
178
|
+
releaseResourceReference: string;
|
|
179
|
+
/** 4系: 複数のDisplayTitle */
|
|
180
|
+
displayTitles?: DisplayTitle[];
|
|
181
|
+
/** 4系: ReleaseLabelReference */
|
|
182
|
+
releaseLabelReferences?: string[];
|
|
183
|
+
genre?: Genre;
|
|
184
|
+
}
|
|
185
|
+
interface ReleaseDetailsByTerritory {
|
|
186
|
+
territoryCode: string[];
|
|
187
|
+
displayArtists?: DisplayArtist[];
|
|
188
|
+
displayArtistName?: string;
|
|
189
|
+
titles?: Title[];
|
|
190
|
+
labelName?: string;
|
|
191
|
+
genre?: Genre;
|
|
192
|
+
parentalWarningType?: string;
|
|
193
|
+
originalReleaseDate?: string;
|
|
194
|
+
resourceGroup?: ResourceGroup;
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/types/deal.d.ts
|
|
198
|
+
interface ReleaseDeal {
|
|
199
|
+
dealReleaseReferences: string[];
|
|
200
|
+
deals: Deal[];
|
|
201
|
+
effectiveDate?: string;
|
|
202
|
+
}
|
|
203
|
+
interface Deal {
|
|
204
|
+
dealReference?: string;
|
|
205
|
+
dealTerms: DealTerms;
|
|
206
|
+
}
|
|
207
|
+
interface DealTerms {
|
|
208
|
+
commercialModelType?: string;
|
|
209
|
+
/** 3.8系: Usage > UseType[] */
|
|
210
|
+
usage?: Usage;
|
|
211
|
+
/** 4系: UseType[] 直下 */
|
|
212
|
+
useTypes?: string[];
|
|
213
|
+
territoryCode?: string[];
|
|
214
|
+
validityPeriod?: ValidityPeriod;
|
|
215
|
+
priceInformation?: PriceInformation;
|
|
216
|
+
takeDown?: boolean;
|
|
217
|
+
}
|
|
218
|
+
interface Usage {
|
|
219
|
+
useTypes: string[];
|
|
220
|
+
}
|
|
221
|
+
interface ValidityPeriod {
|
|
222
|
+
startDate?: string;
|
|
223
|
+
endDate?: string;
|
|
224
|
+
}
|
|
225
|
+
interface PriceInformation {
|
|
226
|
+
priceType?: string;
|
|
227
|
+
wholesalePricePerUnit?: number;
|
|
228
|
+
}
|
|
229
|
+
//#endregion
|
|
230
|
+
//#region src/types/ern.d.ts
|
|
231
|
+
/**
|
|
232
|
+
* パッチレベルまでのバージョン(namespace URIから検出)
|
|
233
|
+
*/
|
|
234
|
+
type ErnVersion = '3.8' | '3.8.1' | '3.8.2' | '3.8.3' | '4.1' | '4.1.1' | '4.2' | '4.3' | '4.3.1' | '4.3.2';
|
|
235
|
+
/**
|
|
236
|
+
* 変換ロジックの分岐用(メジャー系統のみ)
|
|
237
|
+
*/
|
|
238
|
+
type ErnMajorVersion = '3.8' | '4';
|
|
239
|
+
interface MessageParty {
|
|
240
|
+
partyId?: string;
|
|
241
|
+
partyIdIsDpid?: boolean;
|
|
242
|
+
fullName?: string;
|
|
243
|
+
tradingName?: string;
|
|
244
|
+
}
|
|
245
|
+
interface MessageHeader {
|
|
246
|
+
messageThreadId?: string;
|
|
247
|
+
messageId: string;
|
|
248
|
+
messageFileName?: string;
|
|
249
|
+
messageSender: MessageParty;
|
|
250
|
+
messageRecipient: MessageParty;
|
|
251
|
+
messageCreatedDateTime: string;
|
|
252
|
+
}
|
|
253
|
+
interface DdexMessage {
|
|
254
|
+
ernVersion: ErnVersion;
|
|
255
|
+
messageHeader: MessageHeader;
|
|
256
|
+
updateIndicator?: string;
|
|
257
|
+
resourceList: SoundRecording[];
|
|
258
|
+
releaseList: Release[];
|
|
259
|
+
dealList: ReleaseDeal[];
|
|
260
|
+
/** 4系のみ: PartyList(ラウンドトリップ用に保持) */
|
|
261
|
+
partyList?: Party[];
|
|
262
|
+
/** 4系のみ: TrackRelease */
|
|
263
|
+
trackReleaseList?: TrackRelease[];
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/converter/xml-to-json/index.d.ts
|
|
267
|
+
/**
|
|
268
|
+
* XML文字列をDdexMessageに変換する
|
|
269
|
+
*/
|
|
270
|
+
declare function xmlToJson(xml: string): DdexMessage;
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/converter/json-to-xml/index.d.ts
|
|
273
|
+
/**
|
|
274
|
+
* DdexMessageをXML文字列に変換する
|
|
275
|
+
*/
|
|
276
|
+
declare function jsonToXml(message: DdexMessage, version?: ErnVersion): string;
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/version/detect.d.ts
|
|
279
|
+
/**
|
|
280
|
+
* XMLの先頭部分からERNバージョンを検出する
|
|
281
|
+
*
|
|
282
|
+
* 検出戦略(Ruby実装 sshaw/ddex を参考):
|
|
283
|
+
* 1. namespace URI から検出(4系、3.8系共通)
|
|
284
|
+
* 2. MessageSchemaVersionId 属性から検出(3.8系のフォールバック)
|
|
285
|
+
*
|
|
286
|
+
* 巨大ファイル対策として先頭1024バイトのみ読む
|
|
287
|
+
*/
|
|
288
|
+
declare function detectVersion(xml: string): ErnVersion;
|
|
289
|
+
//#endregion
|
|
290
|
+
export { type Artist, type CLine, type Contributor, type DdexMessage, type Deal, type DealTerms, type DisplayArtist, type DisplayTitle, type ErnMajorVersion, type ErnVersion, type Genre, type IndirectResourceContributor, type MessageHeader, type MessageParty, type PLine, type Party, type Release, type ReleaseDeal, type ReleaseResourceReference, type ResourceContributor, type ResourceGroup, type SoundRecording, type TextWithAttribute, type Title, type TrackRelease, type Usage, detectVersion, jsonToXml, xmlToJson };
|