dgc-core 2.0.8 → 2.0.9

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/README.md CHANGED
@@ -97,3 +97,24 @@ The shared RevenueCat catalog maps Rookie, Collector and Elite entitlement IDs f
97
97
  `createRevenueCatClient` accepts server-provided credentials and request limits. `verifyCreditGiveawayEntitlement` checks all eligible tiers using one paginated customer lookup. Null expiry represents non-expiring access; expired entitlements are excluded. Network, rate-limit and malformed-response failures throw a sanitized 503 without exposing credentials. The active entitlements endpoint has no environment filter; configure RevenueCat Sandbox Testing Access and test-account policy for the selected project.
98
98
 
99
99
  Credit winner selection, confirmation and replacement require an injected `checkCreditEligibility(userId)` callback in the optional third `GiveawayRepository` constructor argument. Missing verification is a 503, never a fallback to saved membership. Product operations do not call the verifier. Checks occur outside transactions; reviewed revisions, candidate identity and unexpired evidence are validated at commit. Random selection picks one candidate first, then checks their entitlement. Inactive candidates are retried without replacement up to `creditWinnerMaxAttempts` (default 5; integer 1-100). It never checks the whole entrant pool in advance. The 30-second draw budget remains in place. Transient RevenueCat network/429/5xx failures retry the same user and page up to three attempts with bounded backoff and Retry-After handling, sharing the existing total lookup deadline. Authorization, missing-resource and malformed-response failures are not retried or treated as inactive access. Evidence is stored on selections and winners. Existing confirmed claims and idempotent rewards do not recheck access.
100
+
101
+ ## Public mobile API contracts
102
+
103
+ PriceChartingApiCard and PriceChartingVariantApiCard are defined in src/types/api.ts and remain available through dgc-core/types.
104
+
105
+ The browser-safe types entry exports the public card, set, pricing, image-search,
106
+ population and grading-response contracts from src/types/api.ts. They reuse
107
+ the existing response envelopes and PriceCharting types while keeping nullable
108
+ image-search fields separate from catalog cards. ApiCardResponse, ApiCardsResponse,
109
+ ApiMarketHistoryResponse and the other response aliases model the actual HTTP
110
+ envelopes without exposing stored database records.
111
+
112
+ The dgc-web documentation generator reads these TypeScript contracts and preserves
113
+ its client-facing descriptions as separate annotations. Change shared model fields
114
+ here, then sync the dashboard's generated documentation. The package export tests
115
+ and mobile API compile tests verify public availability, nullability, literal
116
+ statuses and the browser import boundary.
117
+
118
+ These additions follow the existing package release process. Publish a new core
119
+ version before registry consumers import the new aliases; documentation can be
120
+ generated from the reviewed local checkout before that release.
@@ -61,9 +61,9 @@ export declare class GiveawayRepository {
61
61
  private advanceGiveaway;
62
62
  submitEntry(account: UserProfile, id: string, raw: unknown): Promise<{
63
63
  _id: string;
64
+ name: string;
64
65
  email: string;
65
66
  _collectorType: UserCollectorType | null;
66
- name: string;
67
67
  giveawayId: import("../types").GiveawayObjectId;
68
68
  country?: string | undefined;
69
69
  enteredAt: string;
@@ -1,3 +1,5 @@
1
+ import type { CardType } from '../enums/card';
2
+ import type { PriceChartingImages, PriceChartingPrice } from './pricecharting';
1
3
  /** Successful non-paginated DGC API response. */
2
4
  export type ApiResponse<T> = {
3
5
  data: T;
@@ -10,3 +12,187 @@ export type PaginatedApiResponse<T> = {
10
12
  totalCount: number;
11
13
  data: T[];
12
14
  };
15
+ export type PriceChartingApiCard = {
16
+ priceChartingId: number;
17
+ name: string;
18
+ set: string;
19
+ number: string | null;
20
+ releaseDate?: string;
21
+ price: PriceChartingPrice;
22
+ images?: PriceChartingImages;
23
+ type: CardType;
24
+ version: string;
25
+ };
26
+ export type PriceChartingVariantApiCard = PriceChartingApiCard & {
27
+ variant: string;
28
+ };
29
+ /** An unsuccessful API response. Use the HTTP status and message together. */
30
+ export type ApiError = {
31
+ /** Message explaining why the request failed. */
32
+ error: string;
33
+ };
34
+ /** A grading change was accepted. Read the card again to show its current state. */
35
+ export type ApiSuccess = {
36
+ success: true;
37
+ };
38
+ /** Available card image URLs. Missing image sizes are omitted. */
39
+ export type ApiImages = NonNullable<PriceChartingApiCard['images']>;
40
+ /** Prices in cents. Unavailable prices are null; missing price fields are omitted. */
41
+ export type ApiPrice = Partial<PriceChartingPrice>;
42
+ /** Card details for catalog screens. Optional fields are omitted when unavailable. */
43
+ export type ApiCardCore = Partial<Omit<PriceChartingApiCard, 'price' | 'type'>> & {
44
+ /** Available prices in cents. */
45
+ price?: ApiPrice;
46
+ /** Card type, such as pokemon or baseball. */
47
+ type?: string;
48
+ };
49
+ /** Card details with an optional link to its set. */
50
+ export type ApiCard = ApiCardCore & {
51
+ /** Set ID to use with /sets/{id}/cards. */
52
+ setId?: string;
53
+ };
54
+ /** A batch card result with its printing label when available. */
55
+ export type ApiCardWithVariant = ApiCard & Partial<Pick<PriceChartingVariantApiCard, 'variant'>>;
56
+ /** A card variant returned for a set, without a separate setId field. */
57
+ export type ApiSetCardVariant = ApiCardCore & Partial<Pick<PriceChartingVariantApiCard, 'variant'>>;
58
+ /** Paginated card search results. totalCount is capped at 1000. */
59
+ export type ApiCardPage = PaginatedApiResponse<ApiCard>;
60
+ /** Paginated cards in a set. totalCount is the full matching count. */
61
+ export type ApiSetCardPage = PaginatedApiResponse<ApiCardCore>;
62
+ /** Card IDs for a batch detail request. Supply at least one numeric ID. */
63
+ export type ApiBatchRequest = {
64
+ ids: number[];
65
+ };
66
+ /** One legacy trading-card price observation. */
67
+ export type ApiLegacyHistoryEntry = {
68
+ /** Price date, normally YYYY-MM-DD. */
69
+ date: string;
70
+ /** averageSellPrice is in dollars; the other values are zero placeholders. */
71
+ cardmarket: {
72
+ averageSellPrice?: number;
73
+ avg1?: 0;
74
+ avg30?: 0;
75
+ avg7?: 0;
76
+ germanProLow?: 0;
77
+ lowPrice?: 0;
78
+ lowPriceExPlus?: 0;
79
+ reverseHoloAvg1?: 0;
80
+ reverseHoloAvg30?: 0;
81
+ reverseHoloAvg7?: 0;
82
+ reverseHoloLow?: 0;
83
+ reverseHoloSell?: 0;
84
+ reverseHoloTrend?: 0;
85
+ suggestedPrice?: 0;
86
+ trendPrice?: 0;
87
+ };
88
+ };
89
+ /** One market-price observation for a UTC date. Prices are cents or null. */
90
+ export type ApiMarketHistoryEntry = {
91
+ date: string;
92
+ price: Partial<Pick<PriceChartingPrice, 'loose' | 'retailLooseBuy' | 'retailLooseSell'>>;
93
+ };
94
+ /** The selected card, condition, printing and language for a price chart. */
95
+ export type ApiConditionHistoryMeta = {
96
+ /** Requested ID: a number for numeric IDs, or a string for legacy Pokemon IDs. */
97
+ assetId: number | string;
98
+ priceChartingId: number;
99
+ tcgplayerId: string;
100
+ /** ID of the pricing variant used for this chart. */
101
+ id: string;
102
+ /** Full condition name, such as Near Mint. */
103
+ condition: string;
104
+ printing: string;
105
+ language: string;
106
+ /** Current price in cents, or null when unavailable. */
107
+ price: number | null;
108
+ /** Last price update as Unix time in seconds, or null when unavailable. */
109
+ lastUpdated: number | null;
110
+ };
111
+ /** Condition-specific prices, ordered oldest to newest, and chart labels. */
112
+ export type ApiConditionHistoryResponse = ApiResponse<{
113
+ /** UTC price date in YYYY-MM-DD format. */
114
+ date: string;
115
+ /** Price in cents, or null when unavailable. */
116
+ price: Pick<PriceChartingPrice, 'loose'>;
117
+ }[]> & {
118
+ meta: ApiConditionHistoryMeta;
119
+ };
120
+ /** A master set available for browsing. */
121
+ export type ApiMasterSet = {
122
+ /** 24-character set ID to use with /sets/{id}/cards. */
123
+ id: string;
124
+ name: string;
125
+ /** Card type, such as pokemon. */
126
+ type: string;
127
+ releaseDate?: string;
128
+ logo?: string;
129
+ };
130
+ /** Paginated set search results. totalCount includes matches outside this page. */
131
+ export type ApiMasterSetPage = PaginatedApiResponse<ApiMasterSet>;
132
+ /** The master set available for one requested card ID. */
133
+ export type ApiMasterRelationship = {
134
+ assetId: number;
135
+ /** Master-set ID, or null when no master set is available. */
136
+ masterIdentifier: string | null;
137
+ isExist: boolean;
138
+ };
139
+ /** A catalog group and its master sets. Empty groups have an empty sets array. */
140
+ export type ApiGroup = {
141
+ name: string;
142
+ sets: ApiMasterSet[];
143
+ };
144
+ type MaterializedApiImages = {
145
+ [Key in keyof ApiImages]: ApiImages[Key];
146
+ };
147
+ interface ApiImageMetadata extends MaterializedApiImages {
148
+ [key: string]: unknown;
149
+ }
150
+ /** Card identified from a photo. Required nullable fields remain present as null. */
151
+ export type ApiImageCard = Pick<PriceChartingApiCard, 'priceChartingId' | 'name' | 'number' | 'version'> & {
152
+ set: string | null;
153
+ releaseDate: string | null;
154
+ /** Available prices in cents, or null when unavailable. */
155
+ price: Record<string, unknown> | null;
156
+ images: ApiImageMetadata | null;
157
+ type: string | null;
158
+ /** Set ID to use with /sets/{id}/cards. Omitted when unavailable. */
159
+ setId?: string;
160
+ };
161
+ /** Photo search result. Open data only when matched is true. */
162
+ export type ApiImageMatch = {
163
+ matched: boolean;
164
+ reason?: string;
165
+ score: number | null;
166
+ gap: number | null;
167
+ data: ApiImageCard | null;
168
+ };
169
+ /** Card count and percentage for one grade group. */
170
+ export type ApiPopulationBucket = {
171
+ /** Grade from 0.5 to 10 in half-point steps. */
172
+ score: number;
173
+ count: number;
174
+ /** Percentage of the report total, rounded to two decimal places. */
175
+ percent: number;
176
+ };
177
+ /** Exactly 20 grade groups, ordered from 0.5 to 10, including empty groups. */
178
+ export type ApiPopulation = {
179
+ status: 'success';
180
+ data: ApiPopulationBucket[];
181
+ };
182
+ /** Card details in the standard data envelope. */
183
+ export type ApiCardResponse = ApiResponse<ApiCard>;
184
+ /** Batch card details with variant labels in the standard data envelope. */
185
+ export type ApiCardsResponse = ApiResponse<ApiCardWithVariant[]>;
186
+ /** Legacy price observations in the priceData envelope. */
187
+ export type ApiLegacyHistoryResponse = {
188
+ priceData: ApiLegacyHistoryEntry[];
189
+ };
190
+ /** Market-price observations in the standard data envelope. */
191
+ export type ApiMarketHistoryResponse = ApiResponse<ApiMarketHistoryEntry[]>;
192
+ /** Card variants in the standard data envelope. */
193
+ export type ApiVariantsResponse = ApiResponse<ApiSetCardVariant[]>;
194
+ /** Master-set relationships in the standard data envelope. */
195
+ export type ApiMasterRelationshipsResponse = ApiResponse<ApiMasterRelationship[]>;
196
+ /** Catalog groups and their master sets in the standard data envelope. */
197
+ export type ApiGroupsResponse = ApiResponse<ApiGroup[]>;
198
+ export {};
@@ -27,7 +27,7 @@ export type SalesVolumeEntry = {
27
27
  date: string;
28
28
  salesVolume: SalesVolume;
29
29
  };
30
- type Image = {
30
+ export type PriceChartingImages = {
31
31
  tiny?: string;
32
32
  small?: string;
33
33
  normal?: string;
@@ -46,7 +46,7 @@ export type PriceChartingDbCard = {
46
46
  priceHistory: PriceChartingPriceEntry[];
47
47
  salesVolume: SalesVolume;
48
48
  salesVolumeHistory: SalesVolumeEntry[];
49
- images?: Image;
49
+ images?: PriceChartingImages;
50
50
  uploadStatus?: {
51
51
  imagesUploaded?: boolean;
52
52
  noImage?: boolean;
@@ -62,20 +62,6 @@ export type PriceChartingDbCardDto = Omit<PriceChartingDbCard, 'createdAt' | 'up
62
62
  createdAt?: string;
63
63
  updatedAt: string;
64
64
  };
65
- export type PriceChartingApiCard = {
66
- priceChartingId: number;
67
- name: string;
68
- set: string;
69
- number: string | null;
70
- releaseDate?: string;
71
- price: PriceChartingPrice;
72
- images?: Image;
73
- type: CardType;
74
- version: string;
75
- };
76
- export type PriceChartingVariantApiCard = PriceChartingApiCard & {
77
- variant: string;
78
- };
79
65
  export type PriceChartingDbSet = {
80
66
  type: CardType;
81
67
  consoleName: string;
@@ -83,4 +69,3 @@ export type PriceChartingDbSet = {
83
69
  createdAt: Date;
84
70
  updatedAt: Date;
85
71
  };
86
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dgc-core",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "Shared DGC contracts, business rules and server utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",