@kubun/connector-google-contacts 0.10.0 → 0.12.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/lib/actions.js CHANGED
@@ -1 +1,20 @@
1
- import{createContact as e,updateContact as c}from"./api.js";export function createGoogleContactsActionHandler(t){return{create:async(c,a)=>e({accessToken:t.credential.accessToken,params:a,fetch:t.fetch}),update:async(e,a,n)=>c({accessToken:t.credential.accessToken,sourceID:a,params:n,fetch:t.fetch})}}
1
+ import { createContact, updateContact } from './api.js';
2
+ export function createGoogleContactsActionHandler(config) {
3
+ return {
4
+ async create (_model, params) {
5
+ return createContact({
6
+ accessToken: config.credential.accessToken,
7
+ params,
8
+ fetch: config.fetch
9
+ });
10
+ },
11
+ async update (_model, sourceID, params) {
12
+ return updateContact({
13
+ accessToken: config.credential.accessToken,
14
+ sourceID,
15
+ params,
16
+ fetch: config.fetch
17
+ });
18
+ }
19
+ };
20
+ }
package/lib/api.js CHANGED
@@ -1 +1,141 @@
1
- let e="names,emailAddresses,phoneNumbers,organizations,photos",o="https://people.googleapis.com/v1";async function a(e){try{let o=await e.json();return o.error?.message??`${e.status} ${e.statusText}`}catch{return`${e.status} ${e.statusText}`}}export function buildListURL(a){let t=new URL(`${o}/people/me/connections`);return t.searchParams.set("personFields",e),t.searchParams.set("pageSize",String(a.pageSize??100)),null!=a.syncToken&&t.searchParams.set("syncToken",a.syncToken),t.searchParams.set("requestSyncToken","true"),null!=a.pageToken&&t.searchParams.set("pageToken",a.pageToken),t.toString()}export function mapPersonToEntity(e){return{sourceService:"google-contacts",sourceID:e.resourceName,displayName:e.names?.[0]?.displayName??"",email:e.emailAddresses?.[0]?.value??void 0,phoneNumber:e.phoneNumbers?.[0]?.value??void 0,organization:e.organizations?.[0]?.name??void 0,jobTitle:e.organizations?.[0]?.title??void 0,photoURL:e.photos?.[0]?.url??void 0,sourceURL:`https://contacts.google.com/person/${e.resourceName.replace("people/","")}`}}export function buildPersonFromParams(e){let o={};return null!=e.displayName&&(o.names=[{givenName:e.displayName}]),null!=e.email&&(o.emailAddresses=[{value:e.email}]),null!=e.phoneNumber&&(o.phoneNumbers=[{value:e.phoneNumber}]),(null!=e.organization||null!=e.jobTitle)&&(o.organizations=[{...null!=e.organization?{name:e.organization}:{},...null!=e.jobTitle?{title:e.jobTitle}:{}}]),o}export async function createContact(t){let n=t.fetch??globalThis.fetch,r=buildPersonFromParams(t.params),s=await n(`${o}/people:createContact?personFields=${e}`,{method:"POST",headers:{Authorization:`Bearer ${t.accessToken}`,"Content-Type":"application/json"},body:JSON.stringify(r)});if(!s.ok){let e=await a(s);throw Error(`Google People API error: ${e}`)}return mapPersonToEntity(await s.json())}export async function updateContact(t){let n=t.fetch??globalThis.fetch,r=`${o}/${t.sourceID}?personFields=${e}`,s=await n(r,{headers:{Authorization:`Bearer ${t.accessToken}`}});if(!s.ok){let e=await a(s);throw Error(`Google People API error: ${e}`)}let i=await s.json(),l=buildPersonFromParams(t.params);l.etag=i.etag;let u=[];null!=l.names&&u.push("names"),null!=l.emailAddresses&&u.push("emailAddresses"),null!=l.phoneNumbers&&u.push("phoneNumbers"),null!=l.organizations&&u.push("organizations");let p=`${o}/${t.sourceID}:updateContact?updatePersonFields=${u.join(",")}&personFields=${e}`,c=await n(p,{method:"PATCH",headers:{Authorization:`Bearer ${t.accessToken}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(!c.ok){let e=await a(c);throw Error(`Google People API error: ${e}`)}return mapPersonToEntity(await c.json())}
1
+ const PERSON_FIELDS = 'names,emailAddresses,phoneNumbers,organizations,photos';
2
+ const CONTACTS_BASE_URL = 'https://people.googleapis.com/v1';
3
+ // A Google People API resource name is `people/{id}`. A sourceID reaches here
4
+ // from caller-controlled GraphQL input and is interpolated straight into the
5
+ // request path, so anything outside this shape (path traversal, extra segments,
6
+ // query injection) is rejected before a URL is built.
7
+ const SOURCE_ID_PATTERN = /^people\/[A-Za-z0-9_-]+$/;
8
+ function assertValidSourceID(sourceID) {
9
+ if (!SOURCE_ID_PATTERN.test(sourceID)) {
10
+ throw new Error(`Invalid contact sourceID: ${JSON.stringify(sourceID)}`);
11
+ }
12
+ }
13
+ async function parseGoogleError(response) {
14
+ try {
15
+ const body = await response.json();
16
+ return body.error?.message ?? `${response.status} ${response.statusText}`;
17
+ } catch {
18
+ return `${response.status} ${response.statusText}`;
19
+ }
20
+ }
21
+ export function buildListURL(params) {
22
+ const url = new URL(`${CONTACTS_BASE_URL}/people/me/connections`);
23
+ url.searchParams.set('personFields', PERSON_FIELDS);
24
+ url.searchParams.set('pageSize', String(params.pageSize ?? 100));
25
+ if (params.syncToken != null) {
26
+ url.searchParams.set('syncToken', params.syncToken);
27
+ url.searchParams.set('requestSyncToken', 'true');
28
+ } else {
29
+ url.searchParams.set('requestSyncToken', 'true');
30
+ }
31
+ if (params.pageToken != null) {
32
+ url.searchParams.set('pageToken', params.pageToken);
33
+ }
34
+ return url.toString();
35
+ }
36
+ export function mapPersonToEntity(person) {
37
+ return {
38
+ sourceService: 'google-contacts',
39
+ sourceID: person.resourceName,
40
+ displayName: person.names?.[0]?.displayName ?? '',
41
+ email: person.emailAddresses?.[0]?.value ?? undefined,
42
+ phoneNumber: person.phoneNumbers?.[0]?.value ?? undefined,
43
+ organization: person.organizations?.[0]?.name ?? undefined,
44
+ jobTitle: person.organizations?.[0]?.title ?? undefined,
45
+ photoURL: person.photos?.[0]?.url ?? undefined,
46
+ sourceURL: `https://contacts.google.com/person/${person.resourceName.replace('people/', '')}`
47
+ };
48
+ }
49
+ export function buildPersonFromParams(params) {
50
+ const person = {};
51
+ if (params.displayName != null) {
52
+ person.names = [
53
+ {
54
+ givenName: params.displayName
55
+ }
56
+ ];
57
+ }
58
+ if (params.email != null) {
59
+ person.emailAddresses = [
60
+ {
61
+ value: params.email
62
+ }
63
+ ];
64
+ }
65
+ if (params.phoneNumber != null) {
66
+ person.phoneNumbers = [
67
+ {
68
+ value: params.phoneNumber
69
+ }
70
+ ];
71
+ }
72
+ if (params.organization != null || params.jobTitle != null) {
73
+ person.organizations = [
74
+ {
75
+ ...params.organization != null ? {
76
+ name: params.organization
77
+ } : {},
78
+ ...params.jobTitle != null ? {
79
+ title: params.jobTitle
80
+ } : {}
81
+ }
82
+ ];
83
+ }
84
+ return person;
85
+ }
86
+ export async function createContact(options) {
87
+ const fetchFn = options.fetch ?? globalThis.fetch;
88
+ const person = buildPersonFromParams(options.params);
89
+ const response = await fetchFn(`${CONTACTS_BASE_URL}/people:createContact?personFields=${PERSON_FIELDS}`, {
90
+ method: 'POST',
91
+ headers: {
92
+ Authorization: `Bearer ${options.accessToken}`,
93
+ 'Content-Type': 'application/json'
94
+ },
95
+ body: JSON.stringify(person)
96
+ });
97
+ if (!response.ok) {
98
+ const errorMessage = await parseGoogleError(response);
99
+ throw new Error(`Google People API error: ${errorMessage}`);
100
+ }
101
+ const created = await response.json();
102
+ return mapPersonToEntity(created);
103
+ }
104
+ export async function updateContact(options) {
105
+ assertValidSourceID(options.sourceID);
106
+ const fetchFn = options.fetch ?? globalThis.fetch;
107
+ // Fetch current person to get etag (required by Google People API for updates)
108
+ const getURL = `${CONTACTS_BASE_URL}/${options.sourceID}?personFields=${PERSON_FIELDS}`;
109
+ const getResponse = await fetchFn(getURL, {
110
+ headers: {
111
+ Authorization: `Bearer ${options.accessToken}`
112
+ }
113
+ });
114
+ if (!getResponse.ok) {
115
+ const errorMessage = await parseGoogleError(getResponse);
116
+ throw new Error(`Google People API error: ${errorMessage}`);
117
+ }
118
+ const current = await getResponse.json();
119
+ const person = buildPersonFromParams(options.params);
120
+ person.etag = current.etag;
121
+ const updatePersonFields = [];
122
+ if (person.names != null) updatePersonFields.push('names');
123
+ if (person.emailAddresses != null) updatePersonFields.push('emailAddresses');
124
+ if (person.phoneNumbers != null) updatePersonFields.push('phoneNumbers');
125
+ if (person.organizations != null) updatePersonFields.push('organizations');
126
+ const url = `${CONTACTS_BASE_URL}/${options.sourceID}:updateContact?updatePersonFields=${updatePersonFields.join(',')}&personFields=${PERSON_FIELDS}`;
127
+ const response = await fetchFn(url, {
128
+ method: 'PATCH',
129
+ headers: {
130
+ Authorization: `Bearer ${options.accessToken}`,
131
+ 'Content-Type': 'application/json'
132
+ },
133
+ body: JSON.stringify(person)
134
+ });
135
+ if (!response.ok) {
136
+ const errorMessage = await parseGoogleError(response);
137
+ throw new Error(`Google People API error: ${errorMessage}`);
138
+ }
139
+ const updated = await response.json();
140
+ return mapPersonToEntity(updated);
141
+ }
package/lib/connector.js CHANGED
@@ -1 +1,97 @@
1
- import{createGoogleContactsActionHandler as e}from"./actions.js";import{googleContactsClusters as t}from"./model.js";import{createGoogleContactsProvider as o}from"./provider.js";export function createGoogleContactsConnector(r){return{name:"google-contacts",version:"1.0.0",clusters:t,syncStrategies:["on-demand"],auth:{provider:"google",scopes:["https://www.googleapis.com/auth/contacts.readonly"],writeScopes:["https://www.googleapis.com/auth/contacts"]},actions:[{name:"create",description:"Create a new Google Contact",entityModel:"GoogleContact",requiresCredential:!0,input:{type:"object",properties:{displayName:{type:"string"},email:{type:"string"},phoneNumber:{type:"string"},organization:{type:"string"},jobTitle:{type:"string"}},required:["displayName"]}},{name:"update",description:"Update an existing Google Contact",entityModel:"GoogleContact",requiresCredential:!0,input:{type:"object",properties:{sourceID:{type:"string"},displayName:{type:"string"},email:{type:"string"},phoneNumber:{type:"string"},organization:{type:"string"},jobTitle:{type:"string"}},required:["sourceID"]}}],actionHandler:t=>e({credential:t.credential,fetch:r?.fetch}),serverProvider:e=>o({credential:e.credential,fetch:r?.fetch})}}
1
+ import { createGoogleContactsActionHandler } from './actions.js';
2
+ import { googleContactsClusters } from './model.js';
3
+ import { createGoogleContactsProvider } from './provider.js';
4
+ export function createGoogleContactsConnector(options) {
5
+ return {
6
+ name: 'google-contacts',
7
+ version: '1.0.0',
8
+ clusters: googleContactsClusters,
9
+ syncStrategies: [
10
+ 'on-demand'
11
+ ],
12
+ auth: {
13
+ provider: 'google',
14
+ scopes: [
15
+ 'https://www.googleapis.com/auth/contacts.readonly'
16
+ ],
17
+ writeScopes: [
18
+ 'https://www.googleapis.com/auth/contacts'
19
+ ]
20
+ },
21
+ actions: [
22
+ {
23
+ name: 'create',
24
+ description: 'Create a new Google Contact',
25
+ entityModel: 'GoogleContact',
26
+ requiresCredential: true,
27
+ input: {
28
+ type: 'object',
29
+ properties: {
30
+ displayName: {
31
+ type: 'string'
32
+ },
33
+ email: {
34
+ type: 'string'
35
+ },
36
+ phoneNumber: {
37
+ type: 'string'
38
+ },
39
+ organization: {
40
+ type: 'string'
41
+ },
42
+ jobTitle: {
43
+ type: 'string'
44
+ }
45
+ },
46
+ required: [
47
+ 'displayName'
48
+ ]
49
+ }
50
+ },
51
+ {
52
+ name: 'update',
53
+ description: 'Update an existing Google Contact',
54
+ entityModel: 'GoogleContact',
55
+ requiresCredential: true,
56
+ input: {
57
+ type: 'object',
58
+ properties: {
59
+ sourceID: {
60
+ type: 'string'
61
+ },
62
+ displayName: {
63
+ type: 'string'
64
+ },
65
+ email: {
66
+ type: 'string'
67
+ },
68
+ phoneNumber: {
69
+ type: 'string'
70
+ },
71
+ organization: {
72
+ type: 'string'
73
+ },
74
+ jobTitle: {
75
+ type: 'string'
76
+ }
77
+ },
78
+ required: [
79
+ 'sourceID'
80
+ ]
81
+ }
82
+ }
83
+ ],
84
+ actionHandler: (config)=>{
85
+ return createGoogleContactsActionHandler({
86
+ credential: config.credential,
87
+ fetch: options?.fetch
88
+ });
89
+ },
90
+ serverProvider: (config)=>{
91
+ return createGoogleContactsProvider({
92
+ credential: config.credential,
93
+ fetch: options?.fetch
94
+ });
95
+ }
96
+ };
97
+ }
package/lib/index.js CHANGED
@@ -1 +1,5 @@
1
- export{createGoogleContactsActionHandler}from"./actions.js";export{buildListURL,buildPersonFromParams,createContact,mapPersonToEntity,updateContact}from"./api.js";export{createGoogleContactsConnector}from"./connector.js";export{googleContactDefinition,googleContactsClusters}from"./model.js";export{createGoogleContactsProvider}from"./provider.js";
1
+ export { createGoogleContactsActionHandler } from './actions.js';
2
+ export { buildListURL, buildPersonFromParams, createContact, mapPersonToEntity, updateContact } from './api.js';
3
+ export { createGoogleContactsConnector } from './connector.js';
4
+ export { googleContactDefinition, googleContactsClusters } from './model.js';
5
+ export { createGoogleContactsProvider } from './provider.js';
package/lib/model.js CHANGED
@@ -1 +1,79 @@
1
- import{connectorClusters as e,connectorModelIDs as t}from"@kubun/connector";import{modelIDs as o,modelsClusters as i}from"@kubun/models";import{GraphBuilder as r}from"@kubun/protocol";import{dateTimeSchema as n,urlSchema as s}from"@kubun/scalars";export const googleContactDefinition={name:"GoogleContact",behavior:"default",interfaces:[t.ExternalEntity,o.ContactKind],schema:{type:"object",properties:{displayName:{type:"string"},sourceService:{$ref:"#/definitions/SourceService"},sourceID:{$ref:"#/definitions/SourceID"},lastSyncedAt:{$ref:"#/definitions/DateTime"},sourceURL:{$ref:"#/definitions/SourceURL"},email:{type:"string"},phoneNumber:{type:"string"},organization:{type:"string"},jobTitle:{type:"string"},photoURL:{$ref:"#/definitions/PhotoURL"}},required:["sourceService","sourceID","lastSyncedAt"],additionalProperties:!1,definitions:{SourceService:{type:"string",title:"SourceService",minLength:1,maxLength:50},SourceID:{type:"string",title:"SourceID",minLength:1,maxLength:500},SourceURL:s,PhotoURL:s,DateTime:n}},fieldsMeta:{}};let c=new r({externalClusters:{...e,...i}});c.add(googleContactDefinition);export const googleContactsClusters=c.build();
1
+ import { connectorClusters, connectorModelIDs } from '@kubun/connector';
2
+ import { modelIDs, modelsClusters } from '@kubun/models';
3
+ import { GraphBuilder } from '@kubun/protocol';
4
+ import { dateTimeSchema, urlSchema } from '@kubun/scalars';
5
+ export const googleContactDefinition = {
6
+ name: 'GoogleContact',
7
+ behavior: 'default',
8
+ interfaces: [
9
+ connectorModelIDs.ExternalEntity,
10
+ modelIDs.ContactKind
11
+ ],
12
+ schema: {
13
+ type: 'object',
14
+ properties: {
15
+ displayName: {
16
+ type: 'string'
17
+ },
18
+ sourceService: {
19
+ $ref: '#/definitions/SourceService'
20
+ },
21
+ sourceID: {
22
+ $ref: '#/definitions/SourceID'
23
+ },
24
+ lastSyncedAt: {
25
+ $ref: '#/definitions/DateTime'
26
+ },
27
+ sourceURL: {
28
+ $ref: '#/definitions/SourceURL'
29
+ },
30
+ email: {
31
+ type: 'string'
32
+ },
33
+ phoneNumber: {
34
+ type: 'string'
35
+ },
36
+ organization: {
37
+ type: 'string'
38
+ },
39
+ jobTitle: {
40
+ type: 'string'
41
+ },
42
+ photoURL: {
43
+ $ref: '#/definitions/PhotoURL'
44
+ }
45
+ },
46
+ required: [
47
+ 'sourceService',
48
+ 'sourceID',
49
+ 'lastSyncedAt'
50
+ ],
51
+ additionalProperties: false,
52
+ definitions: {
53
+ SourceService: {
54
+ type: 'string',
55
+ title: 'SourceService',
56
+ minLength: 1,
57
+ maxLength: 50
58
+ },
59
+ SourceID: {
60
+ type: 'string',
61
+ title: 'SourceID',
62
+ minLength: 1,
63
+ maxLength: 500
64
+ },
65
+ SourceURL: urlSchema,
66
+ PhotoURL: urlSchema,
67
+ DateTime: dateTimeSchema
68
+ }
69
+ },
70
+ fieldsMeta: {}
71
+ };
72
+ const builder = new GraphBuilder({
73
+ externalClusters: {
74
+ ...connectorClusters,
75
+ ...modelsClusters
76
+ }
77
+ });
78
+ builder.add(googleContactDefinition);
79
+ export const googleContactsClusters = builder.build();
package/lib/provider.js CHANGED
@@ -1 +1,71 @@
1
- import{resolveModelID as e}from"@kubun/connector";import{buildListURL as n,mapPersonToEntity as o}from"./api.js";import{googleContactsClusters as t}from"./model.js";let l=e(t,"GoogleContact");export function createGoogleContactsProvider(e){let t=e.fetch??globalThis.fetch;async function a(o){let l=n({pageToken:o.pageToken,syncToken:o.syncToken}),a=await t(l,{headers:{Authorization:`Bearer ${e.credential.accessToken}`},signal:o.signal});if(!a.ok)throw Error(`Google People API error: ${a.status} ${a.statusText}`);return await a.json()}async function*i(e){let n,t;do{let i=await a({pageToken:n,signal:e.signal}),c=(i.connections??[]).map(o);n=i.nextPageToken,t=i.nextSyncToken,yield{modelID:l,entities:c,hasMore:null!=n,checkpoint:t}}while(null!=n)}return{supportedStrategies:["on-demand"],fetchAll:i,async *fetchChanges(e){let n,t=e.checkpoint;if(null==t)return void(yield*i(e));do{let i=await a({pageToken:n,syncToken:null!=n?void 0:t,signal:e.signal}),c=(i.connections??[]).map(o);n=i.nextPageToken,yield{modelID:l,entities:c,hasMore:null!=n,checkpoint:i.nextSyncToken}}while(null!=n)}}}
1
+ import { resolveModelID } from '@kubun/connector';
2
+ import { buildListURL, mapPersonToEntity } from './api.js';
3
+ import { googleContactsClusters } from './model.js';
4
+ const GOOGLE_CONTACT_MODEL_ID = resolveModelID(googleContactsClusters, 'GoogleContact');
5
+ export function createGoogleContactsProvider(config) {
6
+ const fetchFn = config.fetch ?? globalThis.fetch;
7
+ async function fetchPage(params) {
8
+ const url = buildListURL({
9
+ pageToken: params.pageToken,
10
+ syncToken: params.syncToken
11
+ });
12
+ const response = await fetchFn(url, {
13
+ headers: {
14
+ Authorization: `Bearer ${config.credential.accessToken}`
15
+ },
16
+ signal: params.signal
17
+ });
18
+ if (!response.ok) {
19
+ throw new Error(`Google People API error: ${response.status} ${response.statusText}`);
20
+ }
21
+ return await response.json();
22
+ }
23
+ async function* fetchAllEntities(params) {
24
+ let pageToken;
25
+ let syncToken;
26
+ do {
27
+ const data = await fetchPage({
28
+ pageToken,
29
+ signal: params.signal
30
+ });
31
+ const entities = (data.connections ?? []).map(mapPersonToEntity);
32
+ pageToken = data.nextPageToken;
33
+ syncToken = data.nextSyncToken;
34
+ yield {
35
+ modelID: GOOGLE_CONTACT_MODEL_ID,
36
+ entities,
37
+ hasMore: pageToken != null,
38
+ checkpoint: syncToken
39
+ };
40
+ }while (pageToken != null)
41
+ }
42
+ return {
43
+ supportedStrategies: [
44
+ 'on-demand'
45
+ ],
46
+ fetchAll: fetchAllEntities,
47
+ async *fetchChanges (params) {
48
+ const syncToken = params.checkpoint;
49
+ if (syncToken == null) {
50
+ yield* fetchAllEntities(params);
51
+ return;
52
+ }
53
+ let pageToken;
54
+ do {
55
+ const data = await fetchPage({
56
+ pageToken,
57
+ syncToken: pageToken != null ? undefined : syncToken,
58
+ signal: params.signal
59
+ });
60
+ const entities = (data.connections ?? []).map(mapPersonToEntity);
61
+ pageToken = data.nextPageToken;
62
+ yield {
63
+ modelID: GOOGLE_CONTACT_MODEL_ID,
64
+ entities,
65
+ hasMore: pageToken != null,
66
+ checkpoint: data.nextSyncToken
67
+ };
68
+ }while (pageToken != null)
69
+ }
70
+ };
71
+ }
package/package.json CHANGED
@@ -1,34 +1,34 @@
1
1
  {
2
2
  "name": "@kubun/connector-google-contacts",
3
- "version": "0.10.0",
4
- "license": "see LICENSE.md",
3
+ "version": "0.12.0",
5
4
  "keywords": [],
5
+ "license": "see LICENSE.md",
6
+ "sideEffects": false,
6
7
  "type": "module",
7
- "main": "lib/index.js",
8
- "types": "lib/index.d.ts",
9
8
  "exports": {
10
9
  ".": "./lib/index.js"
11
10
  },
11
+ "main": "lib/index.js",
12
+ "types": "lib/index.d.ts",
12
13
  "files": [
13
14
  "lib/*",
14
15
  "LICENSE.md"
15
16
  ],
16
- "sideEffects": false,
17
17
  "dependencies": {
18
- "@kubun/models": "^0.10.0",
19
- "@kubun/protocol": "^0.10.0",
20
- "@kubun/connector": "^0.10.0",
21
- "@kubun/provider-google": "^0.10.0",
22
- "@kubun/scalars": "^0.10.0"
18
+ "@kubun/connector": "^0.12.0",
19
+ "@kubun/models": "^0.12.0",
20
+ "@kubun/protocol": "^0.12.0",
21
+ "@kubun/scalars": "^0.12.0",
22
+ "@kubun/provider-google": "^0.12.0"
23
23
  },
24
24
  "scripts": {
25
+ "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
25
26
  "build:clean": "del lib",
26
- "build:js": "swc src -d ./lib --config-file ../../swc.json --strip-leading-paths",
27
+ "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
27
28
  "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
28
29
  "build:types:ci": "tsc --emitDeclarationOnly --declarationMap false",
29
- "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
30
+ "test": "pnpm run test:types && pnpm run test:unit",
30
31
  "test:types": "tsc --noEmit -p tsconfig.test.json",
31
- "test:unit": "vitest run",
32
- "test": "pnpm run test:types && pnpm run test:unit"
32
+ "test:unit": "vitest run"
33
33
  }
34
34
  }