@withwiz/coupon 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 +21 -0
- package/README.md +298 -0
- package/dist/index.cjs +1928 -0
- package/dist/index.d.cts +755 -0
- package/dist/index.d.ts +755 -0
- package/dist/index.mjs +1897 -0
- package/package.json +86 -0
- package/prisma/fragment.prisma +122 -0
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packageManager": "pnpm@11.10.0",
|
|
3
|
+
"name": "@withwiz/coupon",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Multi-tenant, concurrency-safe coupon domain core for Prisma + Node.js applications",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "withwiz",
|
|
8
|
+
"private": false,
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/greeun/withwiz-coupon.git"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/greeun/withwiz-coupon#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/greeun/withwiz-coupon/issues"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"coupon",
|
|
22
|
+
"discount",
|
|
23
|
+
"promotion",
|
|
24
|
+
"campaign",
|
|
25
|
+
"prisma",
|
|
26
|
+
"multi-tenant",
|
|
27
|
+
"concurrency"
|
|
28
|
+
],
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.mjs",
|
|
38
|
+
"require": "./dist/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"prisma/fragment.prisma",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.17.0"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"test:coverage": "vitest run --coverage",
|
|
57
|
+
"test:concurrency:x3": "node scripts/check-concurrency-x3.mjs",
|
|
58
|
+
"check:isolation": "node scripts/check-isolation.mjs",
|
|
59
|
+
"prisma:generate": "prisma generate --schema=prisma/schema.prisma",
|
|
60
|
+
"db:push": "prisma db push --schema=prisma/schema.prisma --skip-generate --accept-data-loss",
|
|
61
|
+
"prepublishOnly": "pnpm run build"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"zod": "^3.23.0",
|
|
65
|
+
"nanoid": "^5.0.0",
|
|
66
|
+
"@paralleldrive/cuid2": "^2.2.2"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"@prisma/client": ">=5.0.0 <8.0.0",
|
|
70
|
+
"typescript": ">=5.0.0"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"typescript": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"devDependencies": {
|
|
78
|
+
"@prisma/client": "^6.0.0",
|
|
79
|
+
"@types/node": "^20.12.0",
|
|
80
|
+
"@vitest/coverage-v8": "^1.6.0",
|
|
81
|
+
"prisma": "^6.0.0",
|
|
82
|
+
"tsup": "^8.0.0",
|
|
83
|
+
"typescript": "^5.4.0",
|
|
84
|
+
"vitest": "^1.6.0"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// @withwiz/coupon — schema fragment
|
|
3
|
+
//
|
|
4
|
+
// Copy-paste into your schema.prisma. Model names may be prefixed when they
|
|
5
|
+
// collide with your own schema (`Campaign` is the usual one) — declare the
|
|
6
|
+
// mapping through createCouponClient({ models, tables }). Fields, indexes and
|
|
7
|
+
// @@unique constraints must be kept as they are: the concurrency guarantees
|
|
8
|
+
// depend on them. See "Schema Setup" in README.md.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
model Coupon {
|
|
12
|
+
id String @id @default(cuid())
|
|
13
|
+
tenantId String @db.VarChar(64)
|
|
14
|
+
campaignId String?
|
|
15
|
+
code String @db.VarChar(64)
|
|
16
|
+
status CouponStatus @default(ACTIVE)
|
|
17
|
+
discount Json // DiscountPolicy (PERCENT | FIXED)
|
|
18
|
+
startsAt DateTime?
|
|
19
|
+
endsAt DateTime?
|
|
20
|
+
maxRedemptions Int?
|
|
21
|
+
redeemedCount Int @default(0)
|
|
22
|
+
maxRedemptionsPerUser Int?
|
|
23
|
+
eligibility Json // EligibilityRules
|
|
24
|
+
metadata Json?
|
|
25
|
+
createdAt DateTime @default(now())
|
|
26
|
+
updatedAt DateTime @updatedAt
|
|
27
|
+
|
|
28
|
+
campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: SetNull)
|
|
29
|
+
issuances CouponIssuance[]
|
|
30
|
+
redemptions CouponRedemption[]
|
|
31
|
+
|
|
32
|
+
@@unique([tenantId, code])
|
|
33
|
+
@@index([tenantId, status, endsAt])
|
|
34
|
+
@@index([campaignId])
|
|
35
|
+
@@index([tenantId, createdAt])
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
model Campaign {
|
|
39
|
+
id String @id @default(cuid())
|
|
40
|
+
tenantId String @db.VarChar(64)
|
|
41
|
+
name String @db.VarChar(200)
|
|
42
|
+
status CampaignStatus @default(ACTIVE)
|
|
43
|
+
discount Json
|
|
44
|
+
eligibility Json
|
|
45
|
+
startsAt DateTime?
|
|
46
|
+
endsAt DateTime?
|
|
47
|
+
maxCoupons Int?
|
|
48
|
+
issuedCount Int @default(0)
|
|
49
|
+
metadata Json?
|
|
50
|
+
createdAt DateTime @default(now())
|
|
51
|
+
updatedAt DateTime @updatedAt
|
|
52
|
+
|
|
53
|
+
coupons Coupon[]
|
|
54
|
+
|
|
55
|
+
@@unique([tenantId, name])
|
|
56
|
+
@@index([tenantId, status])
|
|
57
|
+
@@index([tenantId, createdAt])
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
model CouponIssuance {
|
|
61
|
+
id String @id @default(cuid())
|
|
62
|
+
tenantId String @db.VarChar(64)
|
|
63
|
+
couponId String
|
|
64
|
+
issuedToUserId String?
|
|
65
|
+
issuedBy String?
|
|
66
|
+
issuedAt DateTime @default(now())
|
|
67
|
+
metadata Json?
|
|
68
|
+
|
|
69
|
+
coupon Coupon @relation(fields: [couponId], references: [id], onDelete: Cascade)
|
|
70
|
+
|
|
71
|
+
@@index([tenantId, couponId])
|
|
72
|
+
@@index([tenantId, issuedToUserId, issuedAt])
|
|
73
|
+
@@index([issuedAt])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
model CouponRedemption {
|
|
77
|
+
id String @id @default(cuid())
|
|
78
|
+
tenantId String @db.VarChar(64)
|
|
79
|
+
couponId String
|
|
80
|
+
redeemedByUserId String?
|
|
81
|
+
orderRef String? @db.VarChar(128)
|
|
82
|
+
discountAmount Int
|
|
83
|
+
currency String @db.VarChar(3)
|
|
84
|
+
redeemedAt DateTime @default(now())
|
|
85
|
+
metadata Json?
|
|
86
|
+
|
|
87
|
+
coupon Coupon @relation(fields: [couponId], references: [id], onDelete: Cascade)
|
|
88
|
+
|
|
89
|
+
@@unique([couponId, redeemedByUserId, orderRef])
|
|
90
|
+
@@index([tenantId, couponId, redeemedAt])
|
|
91
|
+
@@index([tenantId, redeemedByUserId, redeemedAt])
|
|
92
|
+
@@index([redeemedAt])
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
model CouponAuditLog {
|
|
96
|
+
id String @id @default(cuid())
|
|
97
|
+
tenantId String @db.VarChar(64)
|
|
98
|
+
action String @db.VarChar(50)
|
|
99
|
+
resourceId String? @db.VarChar(64)
|
|
100
|
+
resourceType String? @db.VarChar(30)
|
|
101
|
+
actorId String?
|
|
102
|
+
details Json?
|
|
103
|
+
createdAt DateTime @default(now())
|
|
104
|
+
|
|
105
|
+
@@index([tenantId, createdAt])
|
|
106
|
+
@@index([tenantId, resourceType, resourceId])
|
|
107
|
+
@@index([action])
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
enum CouponStatus {
|
|
111
|
+
DRAFT
|
|
112
|
+
ACTIVE
|
|
113
|
+
PAUSED
|
|
114
|
+
EXPIRED
|
|
115
|
+
EXHAUSTED
|
|
116
|
+
ARCHIVED
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
enum CampaignStatus {
|
|
120
|
+
ACTIVE
|
|
121
|
+
CLOSED
|
|
122
|
+
}
|