@fonderie/customers 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.
@@ -0,0 +1,75 @@
1
+ -- fonderie_customer_label_type enum
2
+ CREATE TYPE fonderie_customer_label_type AS ENUM ('email', 'phone', 'address');
3
+
4
+ -- shared label catalog
5
+ CREATE TABLE IF NOT EXISTS fonderie_customer_labels (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ type fonderie_customer_label_type NOT NULL,
8
+ value TEXT NOT NULL,
9
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
10
+ CONSTRAINT uq_fcl_type_value UNIQUE (type, value)
11
+ );
12
+
13
+ -- seed defaults
14
+ INSERT INTO fonderie_customer_labels (type, value) VALUES
15
+ ('email', 'work'),
16
+ ('email', 'personal'),
17
+ ('email', 'billing'),
18
+ ('phone', 'mobile'),
19
+ ('phone', 'office'),
20
+ ('phone', 'home'),
21
+ ('phone', 'fax'),
22
+ ('address', 'service'),
23
+ ('address', 'billing'),
24
+ ('address', 'other')
25
+ ON CONFLICT (type, value) DO NOTHING;
26
+
27
+ -- promote any non-standard values already stored in existing rows
28
+ INSERT INTO fonderie_customer_labels (type, value)
29
+ SELECT DISTINCT 'email'::fonderie_customer_label_type, label
30
+ FROM fonderie_customer_emails
31
+ WHERE label IS NOT NULL
32
+ ON CONFLICT (type, value) DO NOTHING;
33
+
34
+ INSERT INTO fonderie_customer_labels (type, value)
35
+ SELECT DISTINCT 'phone'::fonderie_customer_label_type, label
36
+ FROM fonderie_customer_phones
37
+ WHERE label IS NOT NULL
38
+ ON CONFLICT (type, value) DO NOTHING;
39
+
40
+ INSERT INTO fonderie_customer_labels (type, value)
41
+ SELECT DISTINCT 'address'::fonderie_customer_label_type, label
42
+ FROM fonderie_customer_addresses
43
+ WHERE label IS NOT NULL
44
+ ON CONFLICT (type, value) DO NOTHING;
45
+
46
+ -- add label_id FK columns
47
+ ALTER TABLE fonderie_customer_emails
48
+ ADD COLUMN IF NOT EXISTS label_id UUID REFERENCES fonderie_customer_labels(id);
49
+
50
+ ALTER TABLE fonderie_customer_phones
51
+ ADD COLUMN IF NOT EXISTS label_id UUID REFERENCES fonderie_customer_labels(id);
52
+
53
+ ALTER TABLE fonderie_customer_addresses
54
+ ADD COLUMN IF NOT EXISTS label_id UUID REFERENCES fonderie_customer_labels(id);
55
+
56
+ -- backfill label_id from existing label text
57
+ UPDATE fonderie_customer_emails e
58
+ SET label_id = l.id
59
+ FROM fonderie_customer_labels l
60
+ WHERE l.type = 'email' AND l.value = e.label;
61
+
62
+ UPDATE fonderie_customer_phones p
63
+ SET label_id = l.id
64
+ FROM fonderie_customer_labels l
65
+ WHERE l.type = 'phone' AND l.value = p.label;
66
+
67
+ UPDATE fonderie_customer_addresses a
68
+ SET label_id = l.id
69
+ FROM fonderie_customer_labels l
70
+ WHERE l.type = 'address' AND l.value = a.label;
71
+
72
+ -- mark old text columns as deprecated
73
+ COMMENT ON COLUMN fonderie_customer_emails.label IS 'deprecated: use label_id → fonderie_customer_labels';
74
+ COMMENT ON COLUMN fonderie_customer_phones.label IS 'deprecated: use label_id → fonderie_customer_labels';
75
+ COMMENT ON COLUMN fonderie_customer_addresses.label IS 'deprecated: use label_id → fonderie_customer_labels';
@@ -0,0 +1,17 @@
1
+ -- Allow label catalog entries to be deleted even when in use.
2
+ -- Affected phone/email/address rows keep their label text column; label_id becomes NULL.
3
+
4
+ ALTER TABLE fonderie_customer_phones
5
+ DROP CONSTRAINT IF EXISTS fonderie_customer_phones_label_id_fkey,
6
+ ADD CONSTRAINT fonderie_customer_phones_label_id_fkey
7
+ FOREIGN KEY (label_id) REFERENCES fonderie_customer_labels(id) ON DELETE SET NULL;
8
+
9
+ ALTER TABLE fonderie_customer_emails
10
+ DROP CONSTRAINT IF EXISTS fonderie_customer_emails_label_id_fkey,
11
+ ADD CONSTRAINT fonderie_customer_emails_label_id_fkey
12
+ FOREIGN KEY (label_id) REFERENCES fonderie_customer_labels(id) ON DELETE SET NULL;
13
+
14
+ ALTER TABLE fonderie_customer_addresses
15
+ DROP CONSTRAINT IF EXISTS fonderie_customer_addresses_label_id_fkey,
16
+ ADD CONSTRAINT fonderie_customer_addresses_label_id_fkey
17
+ FOREIGN KEY (label_id) REFERENCES fonderie_customer_labels(id) ON DELETE SET NULL;
package/dist/types.cjs ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/types.ts
17
+ var types_exports = {};
18
+ module.exports = __toCommonJS(types_exports);
19
+ //# sourceMappingURL=types.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["export type CustomerType = 'individual' | 'business';\nexport type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';\nexport type CustomerLabelType = 'email' | 'phone' | 'address';\n\n/** @deprecated resolved dynamically via fonderie_customer_labels */\nexport type EmailLabel = 'work' | 'personal' | 'billing';\n/** @deprecated resolved dynamically via fonderie_customer_labels */\nexport type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';\n/** @deprecated resolved dynamically via fonderie_customer_labels */\nexport type AddressLabel = 'service' | 'billing' | 'other';\n\nexport interface ICustomerLabel {\n\tid: string;\n\ttype: CustomerLabelType;\n\tvalue: string;\n\tcreatedAt: string;\n}\n\nexport interface ICustomer {\n\tid: string;\n\tworkspaceId: string;\n\ttype: CustomerType;\n\tsex: CustomerSex;\n\tfirstName: string | null;\n\tlastName: string | null;\n\tcompanyName: string | null;\n\tavatarUrl: string | null;\n\tlocale: string;\n\treferenceCode: string | null;\n\tisBlacklisted: boolean;\n\tblacklistReason: string | null;\n\tcreatedBy: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerEmail {\n\tid: string;\n\tcustomerId: string;\n\temail: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerPhone {\n\tid: string;\n\tcustomerId: string;\n\tphone: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface IAddress {\n\tid: string;\n\tcountryIso: string;\n\tsubdivision1Iso: string | null;\n\tsubdivision2Iso: string | null;\n\tzipPostalCode: string;\n\tunit: string | null;\n\tline1: string | null;\n\tline2: string | null;\n}\n\nexport interface ICustomerAddress {\n\taddrId: string;\n\tcustomerId: string;\n\tlabelId: string;\n\tlabel: string;\n\tisPrimary: boolean;\n\taddress: IAddress;\n}\n\nexport interface ICustomerNote {\n\tid: string;\n\tcustomerId: string;\n\tauthorId: string | null;\n\tbody: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface ICustomerTag {\n\tcustomerId: string;\n\ttag: string;\n}\n\nexport interface ICustomerRelationship {\n\tid: string;\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelatedId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n}\n\nexport interface ICustomerShallow extends ICustomer {\n\temails: ICustomerEmail[];\n\tphones: ICustomerPhone[];\n\taddresses: ICustomerAddress[];\n\tnotes: ICustomerNote[];\n\ttags: string[];\n}\n\nexport interface ICustomerRelationshipExpanded {\n\tid: string;\n\tworkspaceId: string;\n\tcustomerId: string;\n\trelationship: string;\n\tisPrimary: boolean;\n\tcreatedAt: string;\n\tcustomer: ICustomerShallow;\n}\n\nexport interface ICustomerDetail extends ICustomer {\n\temails: ICustomerEmail[];\n\tphones: ICustomerPhone[];\n\taddresses: ICustomerAddress[];\n\tnotes: ICustomerNote[];\n\trelationships: ICustomerRelationshipExpanded[];\n\ttags: string[];\n}\n\n// Depth-2 variants: depth-1 customers have their own relationships resolved.\nexport interface ICustomerShallowD2 extends ICustomerShallow {\n\trelationships: ICustomerRelationshipExpanded[];\n}\n\nexport interface ICustomerRelationshipExpandedD2 extends Omit<ICustomerRelationshipExpanded, 'customer'> {\n\tcustomer: ICustomerShallowD2;\n}\n\nexport interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {\n\trelationships: ICustomerRelationshipExpandedD2[];\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,124 @@
1
+ type CustomerType = 'individual' | 'business';
2
+ type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';
3
+ type CustomerLabelType = 'email' | 'phone' | 'address';
4
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
5
+ type EmailLabel = 'work' | 'personal' | 'billing';
6
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
7
+ type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';
8
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
9
+ type AddressLabel = 'service' | 'billing' | 'other';
10
+ interface ICustomerLabel {
11
+ id: string;
12
+ type: CustomerLabelType;
13
+ value: string;
14
+ createdAt: string;
15
+ }
16
+ interface ICustomer {
17
+ id: string;
18
+ workspaceId: string;
19
+ type: CustomerType;
20
+ sex: CustomerSex;
21
+ firstName: string | null;
22
+ lastName: string | null;
23
+ companyName: string | null;
24
+ avatarUrl: string | null;
25
+ locale: string;
26
+ referenceCode: string | null;
27
+ isBlacklisted: boolean;
28
+ blacklistReason: string | null;
29
+ createdBy: string | null;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ }
33
+ interface ICustomerEmail {
34
+ id: string;
35
+ customerId: string;
36
+ email: string;
37
+ labelId: string;
38
+ label: string;
39
+ isPrimary: boolean;
40
+ createdAt: string;
41
+ }
42
+ interface ICustomerPhone {
43
+ id: string;
44
+ customerId: string;
45
+ phone: string;
46
+ labelId: string;
47
+ label: string;
48
+ isPrimary: boolean;
49
+ createdAt: string;
50
+ }
51
+ interface IAddress {
52
+ id: string;
53
+ countryIso: string;
54
+ subdivision1Iso: string | null;
55
+ subdivision2Iso: string | null;
56
+ zipPostalCode: string;
57
+ unit: string | null;
58
+ line1: string | null;
59
+ line2: string | null;
60
+ }
61
+ interface ICustomerAddress {
62
+ addrId: string;
63
+ customerId: string;
64
+ labelId: string;
65
+ label: string;
66
+ isPrimary: boolean;
67
+ address: IAddress;
68
+ }
69
+ interface ICustomerNote {
70
+ id: string;
71
+ customerId: string;
72
+ authorId: string | null;
73
+ body: string;
74
+ createdAt: string;
75
+ updatedAt: string;
76
+ }
77
+ interface ICustomerTag {
78
+ customerId: string;
79
+ tag: string;
80
+ }
81
+ interface ICustomerRelationship {
82
+ id: string;
83
+ workspaceId: string;
84
+ customerId: string;
85
+ relatedId: string;
86
+ relationship: string;
87
+ isPrimary: boolean;
88
+ createdAt: string;
89
+ }
90
+ interface ICustomerShallow extends ICustomer {
91
+ emails: ICustomerEmail[];
92
+ phones: ICustomerPhone[];
93
+ addresses: ICustomerAddress[];
94
+ notes: ICustomerNote[];
95
+ tags: string[];
96
+ }
97
+ interface ICustomerRelationshipExpanded {
98
+ id: string;
99
+ workspaceId: string;
100
+ customerId: string;
101
+ relationship: string;
102
+ isPrimary: boolean;
103
+ createdAt: string;
104
+ customer: ICustomerShallow;
105
+ }
106
+ interface ICustomerDetail extends ICustomer {
107
+ emails: ICustomerEmail[];
108
+ phones: ICustomerPhone[];
109
+ addresses: ICustomerAddress[];
110
+ notes: ICustomerNote[];
111
+ relationships: ICustomerRelationshipExpanded[];
112
+ tags: string[];
113
+ }
114
+ interface ICustomerShallowD2 extends ICustomerShallow {
115
+ relationships: ICustomerRelationshipExpanded[];
116
+ }
117
+ interface ICustomerRelationshipExpandedD2 extends Omit<ICustomerRelationshipExpanded, 'customer'> {
118
+ customer: ICustomerShallowD2;
119
+ }
120
+ interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {
121
+ relationships: ICustomerRelationshipExpandedD2[];
122
+ }
123
+
124
+ export type { AddressLabel, CustomerLabelType, CustomerSex, CustomerType, EmailLabel, IAddress, ICustomer, ICustomerAddress, ICustomerDetail, ICustomerDetailD2, ICustomerEmail, ICustomerLabel, ICustomerNote, ICustomerPhone, ICustomerRelationship, ICustomerRelationshipExpanded, ICustomerRelationshipExpandedD2, ICustomerShallow, ICustomerShallowD2, ICustomerTag, PhoneLabel };
@@ -0,0 +1,124 @@
1
+ type CustomerType = 'individual' | 'business';
2
+ type CustomerSex = 'UNKNOWN' | 'MALE' | 'FEMALE';
3
+ type CustomerLabelType = 'email' | 'phone' | 'address';
4
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
5
+ type EmailLabel = 'work' | 'personal' | 'billing';
6
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
7
+ type PhoneLabel = 'mobile' | 'office' | 'home' | 'fax';
8
+ /** @deprecated resolved dynamically via fonderie_customer_labels */
9
+ type AddressLabel = 'service' | 'billing' | 'other';
10
+ interface ICustomerLabel {
11
+ id: string;
12
+ type: CustomerLabelType;
13
+ value: string;
14
+ createdAt: string;
15
+ }
16
+ interface ICustomer {
17
+ id: string;
18
+ workspaceId: string;
19
+ type: CustomerType;
20
+ sex: CustomerSex;
21
+ firstName: string | null;
22
+ lastName: string | null;
23
+ companyName: string | null;
24
+ avatarUrl: string | null;
25
+ locale: string;
26
+ referenceCode: string | null;
27
+ isBlacklisted: boolean;
28
+ blacklistReason: string | null;
29
+ createdBy: string | null;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ }
33
+ interface ICustomerEmail {
34
+ id: string;
35
+ customerId: string;
36
+ email: string;
37
+ labelId: string;
38
+ label: string;
39
+ isPrimary: boolean;
40
+ createdAt: string;
41
+ }
42
+ interface ICustomerPhone {
43
+ id: string;
44
+ customerId: string;
45
+ phone: string;
46
+ labelId: string;
47
+ label: string;
48
+ isPrimary: boolean;
49
+ createdAt: string;
50
+ }
51
+ interface IAddress {
52
+ id: string;
53
+ countryIso: string;
54
+ subdivision1Iso: string | null;
55
+ subdivision2Iso: string | null;
56
+ zipPostalCode: string;
57
+ unit: string | null;
58
+ line1: string | null;
59
+ line2: string | null;
60
+ }
61
+ interface ICustomerAddress {
62
+ addrId: string;
63
+ customerId: string;
64
+ labelId: string;
65
+ label: string;
66
+ isPrimary: boolean;
67
+ address: IAddress;
68
+ }
69
+ interface ICustomerNote {
70
+ id: string;
71
+ customerId: string;
72
+ authorId: string | null;
73
+ body: string;
74
+ createdAt: string;
75
+ updatedAt: string;
76
+ }
77
+ interface ICustomerTag {
78
+ customerId: string;
79
+ tag: string;
80
+ }
81
+ interface ICustomerRelationship {
82
+ id: string;
83
+ workspaceId: string;
84
+ customerId: string;
85
+ relatedId: string;
86
+ relationship: string;
87
+ isPrimary: boolean;
88
+ createdAt: string;
89
+ }
90
+ interface ICustomerShallow extends ICustomer {
91
+ emails: ICustomerEmail[];
92
+ phones: ICustomerPhone[];
93
+ addresses: ICustomerAddress[];
94
+ notes: ICustomerNote[];
95
+ tags: string[];
96
+ }
97
+ interface ICustomerRelationshipExpanded {
98
+ id: string;
99
+ workspaceId: string;
100
+ customerId: string;
101
+ relationship: string;
102
+ isPrimary: boolean;
103
+ createdAt: string;
104
+ customer: ICustomerShallow;
105
+ }
106
+ interface ICustomerDetail extends ICustomer {
107
+ emails: ICustomerEmail[];
108
+ phones: ICustomerPhone[];
109
+ addresses: ICustomerAddress[];
110
+ notes: ICustomerNote[];
111
+ relationships: ICustomerRelationshipExpanded[];
112
+ tags: string[];
113
+ }
114
+ interface ICustomerShallowD2 extends ICustomerShallow {
115
+ relationships: ICustomerRelationshipExpanded[];
116
+ }
117
+ interface ICustomerRelationshipExpandedD2 extends Omit<ICustomerRelationshipExpanded, 'customer'> {
118
+ customer: ICustomerShallowD2;
119
+ }
120
+ interface ICustomerDetailD2 extends Omit<ICustomerDetail, 'relationships'> {
121
+ relationships: ICustomerRelationshipExpandedD2[];
122
+ }
123
+
124
+ export type { AddressLabel, CustomerLabelType, CustomerSex, CustomerType, EmailLabel, IAddress, ICustomer, ICustomerAddress, ICustomerDetail, ICustomerDetailD2, ICustomerEmail, ICustomerLabel, ICustomerNote, ICustomerPhone, ICustomerRelationship, ICustomerRelationshipExpanded, ICustomerRelationshipExpandedD2, ICustomerShallow, ICustomerShallowD2, ICustomerTag, PhoneLabel };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,85 @@
1
+ {
2
+ "name": "@fonderie/customers",
3
+ "version": "1.0.0",
4
+ "description": "Workspace-scoped customer records — individuals and businesses with multiple emails, phones, addresses, notes, and tags.",
5
+ "keywords": [
6
+ "fonderie-js",
7
+ "customers",
8
+ "saas",
9
+ "typescript"
10
+ ],
11
+ "license": "MIT",
12
+ "type": "module",
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ },
22
+ "./types": {
23
+ "types": "./dist/types.d.ts",
24
+ "import": "./dist/types.js",
25
+ "require": "./dist/types.cjs"
26
+ },
27
+ "./migrations": {
28
+ "types": "./dist/migrations/index.d.ts",
29
+ "import": "./dist/migrations/index.js"
30
+ }
31
+ },
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "scripts": {
36
+ "build": "tsup && node -e \"require('node:fs').cpSync('src/migrations/sql', 'dist/migrations/sql', {recursive:true})\"",
37
+ "dev": "tsup --watch",
38
+ "typecheck": "tsc --noEmit",
39
+ "test": "tsx --test src/__tests__/*.test.ts",
40
+ "lint": "biome lint src",
41
+ "format": "biome format --write src",
42
+ "check": "biome check --write src"
43
+ },
44
+ "peerDependencies": {
45
+ "@fonderie/core": "^0.1.0",
46
+ "@fonderie/events": "^1.0.0",
47
+ "@fonderie/store": "^0.1.0",
48
+ "@fonderie/workspaces": "^1.0.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "@fonderie/events": {
52
+ "optional": true
53
+ },
54
+ "@fonderie/workspaces": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "devDependencies": {
59
+ "@fonderie/core": "../core",
60
+ "@fonderie/events": "../events",
61
+ "@fonderie/store": "../store",
62
+ "@fonderie/workspaces": "../workspaces",
63
+ "@types/node": "^25.6.0",
64
+ "tsup": "^8.5.1",
65
+ "tsx": "^4.21.0",
66
+ "typescript": "^6.0.3"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public"
70
+ },
71
+ "files": [
72
+ "dist",
73
+ "LICENSE",
74
+ "README.md"
75
+ ],
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/fonderie-js/sdk.git",
79
+ "directory": "packages/customers"
80
+ },
81
+ "homepage": "https://github.com/fonderie-js/sdk/tree/main/packages/customers#readme",
82
+ "bugs": {
83
+ "url": "https://github.com/fonderie-js/sdk/issues"
84
+ }
85
+ }