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 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)
@@ -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 };