@superpanel-sdk/runtime 0.2.2 → 0.5.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/README.md +28 -8
- package/dist/builder/codegen/emit.d.ts +29 -11
- package/dist/builder/codegen/emit.js +328 -61
- package/dist/builder/codegen/generate.d.ts +21 -0
- package/dist/builder/codegen/generate.js +152 -20
- package/dist/builder/company-import-graph.d.ts +2 -0
- package/dist/builder/company-import-graph.js +40 -8
- package/dist/builder/company-imports.d.ts +24 -0
- package/dist/builder/company-imports.js +114 -0
- package/dist/builder/compile/compile-sequence.d.ts +59 -8
- package/dist/builder/compile/compile-sequence.js +279 -77
- package/dist/builder/compile/function-code.d.ts +12 -2
- package/dist/builder/compile/function-code.js +87 -39
- package/dist/builder/compile/registry.d.ts +7 -3
- package/dist/builder/compile/registry.js +24 -23
- package/dist/builder/load.d.ts +70 -10
- package/dist/builder/load.js +250 -61
- package/dist/builder/paths.d.ts +3 -0
- package/dist/builder/paths.js +16 -0
- package/dist/builder/schedules.d.ts +23 -0
- package/dist/builder/schedules.js +102 -0
- package/dist/builder/slot-schema-validation.d.ts +4 -0
- package/dist/builder/slot-schema-validation.js +10 -0
- package/dist/builder/typecheck.d.ts +8 -0
- package/dist/builder/typecheck.js +105 -0
- package/dist/builder/types.d.ts +91 -19
- package/dist/builder/types.js +2 -1
- package/dist/cli/auth/auth.d.ts +37 -0
- package/dist/cli/auth/auth.js +211 -0
- package/dist/cli/auth/backend-url.d.ts +5 -0
- package/dist/cli/auth/backend-url.js +25 -0
- package/dist/cli/auth/clipboard.d.ts +9 -0
- package/dist/cli/auth/clipboard.js +38 -0
- package/dist/cli/auth/http.d.ts +20 -0
- package/dist/cli/auth/http.js +27 -0
- package/dist/cli/auth/index.d.ts +10 -0
- package/dist/cli/auth/index.js +27 -0
- package/dist/cli/auth/progress.d.ts +12 -0
- package/dist/cli/auth/progress.js +15 -0
- package/dist/cli/auth/serve-login.d.ts +8 -0
- package/dist/cli/auth/serve-login.js +24 -0
- package/dist/cli/compile/index.js +3 -0
- package/dist/cli/config.d.ts +10 -0
- package/dist/cli/config.js +52 -0
- package/dist/cli/index.js +28 -8
- package/dist/cli/serve/index.d.ts +73 -3
- package/dist/cli/serve/index.js +410 -113
- package/dist/cli/type-gen/index.js +9 -8
- package/dist/compiler.d.ts +7 -0
- package/dist/compiler.js +7 -0
- package/dist/compute-host/compute-host.d.ts +15 -23
- package/dist/compute-host/compute-host.js +25 -28
- package/dist/compute-host/index.d.ts +2 -2
- package/dist/compute-host/index.js +1 -1
- package/dist/compute-host/source-compute-host.d.ts +11 -9
- package/dist/compute-host/source-compute-host.js +50 -31
- package/dist/core/case-orchestrator/contact-hours.d.ts +50 -0
- package/dist/core/case-orchestrator/contact-hours.js +108 -0
- package/dist/core/case-orchestrator/follow-ups.d.ts +31 -0
- package/dist/core/case-orchestrator/follow-ups.js +103 -0
- package/dist/core/case-orchestrator/index.d.ts +6 -0
- package/dist/core/case-orchestrator/index.js +6 -0
- package/dist/core/case-orchestrator/render.d.ts +75 -0
- package/dist/core/case-orchestrator/render.js +239 -0
- package/dist/core/case-orchestrator/time.d.ts +11 -0
- package/dist/core/case-orchestrator/time.js +29 -0
- package/dist/core/case-orchestrator/tools.d.ts +29 -0
- package/dist/core/case-orchestrator/tools.js +31 -0
- package/dist/core/case-orchestrator/types.d.ts +753 -0
- package/dist/core/case-orchestrator/types.js +46 -0
- package/dist/core/case-orchestrator/validate.d.ts +11 -0
- package/dist/core/case-orchestrator/validate.js +63 -0
- package/dist/core/context/call.d.ts +291 -0
- package/dist/core/context/call.js +94 -0
- package/dist/core/context/channels.d.ts +60 -0
- package/dist/core/context/channels.js +35 -0
- package/dist/core/context/contact.d.ts +96 -6
- package/dist/core/context/contact.js +48 -2
- package/dist/core/context/email-message.d.ts +159 -0
- package/dist/core/context/email-message.js +59 -0
- package/dist/core/context/email.d.ts +97 -0
- package/dist/core/context/email.js +91 -0
- package/dist/core/context/exchanges.d.ts +142 -0
- package/dist/core/context/exchanges.js +63 -0
- package/dist/core/context/instances.d.ts +278 -0
- package/dist/core/context/instances.js +123 -0
- package/dist/core/context/kv.d.ts +113 -0
- package/dist/core/context/kv.js +60 -0
- package/dist/core/context/platform-registry.d.ts +509 -4
- package/dist/core/context/platform-registry.js +14 -2
- package/dist/core/context/platform.d.ts +2 -1
- package/dist/core/context/sms.d.ts +147 -0
- package/dist/core/context/sms.js +57 -0
- package/dist/core/context/types.d.ts +51 -30
- package/dist/core/context/types.js +1 -7
- package/dist/core/conversation/index.d.ts +31 -0
- package/dist/core/conversation/index.js +14 -0
- package/dist/core/functions/define/bind.d.ts +25 -0
- package/dist/core/functions/define/bind.js +14 -0
- package/dist/core/functions/define/case.d.ts +9 -0
- package/dist/core/functions/define/case.js +13 -0
- package/dist/core/functions/define/events.d.ts +20 -0
- package/dist/core/functions/define/events.js +21 -0
- package/dist/core/functions/define/no-case.d.ts +4 -0
- package/dist/core/functions/define/no-case.js +9 -0
- package/dist/core/functions/events.d.ts +247 -0
- package/dist/core/functions/events.js +32 -0
- package/dist/core/functions/func-ref.d.ts +11 -2
- package/dist/core/functions/schedules.d.ts +51 -0
- package/dist/core/functions/schedules.js +20 -0
- package/dist/core/functions/types.d.ts +62 -51
- package/dist/core/functions/types.js +6 -1
- package/dist/core/office-hours/index.d.ts +39 -0
- package/dist/core/office-hours/index.js +187 -0
- package/dist/core/page/define.d.ts +834 -0
- package/dist/core/page/define.js +477 -0
- package/dist/core/page/ops.d.ts +58 -0
- package/dist/core/page/ops.js +220 -0
- package/dist/core/page/render.d.ts +253 -0
- package/dist/core/page/render.js +840 -0
- package/dist/core/page/time-format.d.ts +19 -0
- package/dist/core/page/time-format.js +41 -0
- package/dist/core/page/timeline.d.ts +40 -0
- package/dist/core/page/timeline.js +2 -0
- package/dist/core/page/typed.d.ts +642 -0
- package/dist/core/page/typed.js +2 -0
- package/dist/core/page/workspace.d.ts +33 -0
- package/dist/core/page/workspace.js +231 -0
- package/dist/core/party/index.d.ts +3 -0
- package/dist/core/party/index.js +3 -0
- package/dist/core/party/schema.d.ts +137 -0
- package/dist/core/party/schema.js +80 -0
- package/dist/core/party/types.d.ts +40 -0
- package/dist/core/party/types.js +15 -0
- package/dist/core/sequence/define.d.ts +57 -0
- package/dist/core/sequence/define.js +10 -0
- package/dist/core/sequence/index.d.ts +63 -0
- package/dist/core/sequence/index.js +231 -0
- package/dist/core/slot/binding.d.ts +40 -0
- package/dist/core/slot/binding.js +2 -0
- package/dist/core/slot/company-metadata.d.ts +113 -0
- package/dist/core/slot/company-metadata.js +563 -0
- package/dist/core/slot/compile-schema.d.ts +19 -13
- package/dist/core/slot/compile-schema.js +51 -248
- package/dist/core/slot/contact-timezone.d.ts +110 -0
- package/dist/core/slot/contact-timezone.js +186 -0
- package/dist/core/slot/field.d.ts +2 -24
- package/dist/core/slot/field.js +0 -69
- package/dist/core/slot/id-ref.d.ts +17 -2
- package/dist/core/slot/id-ref.js +16 -8
- package/dist/core/slot/index.d.ts +12 -13
- package/dist/core/slot/index.js +11 -12
- package/dist/core/slot/naming.d.ts +15 -0
- package/dist/core/slot/naming.js +96 -0
- package/dist/core/slot/platform-fields.d.ts +119 -0
- package/dist/core/slot/platform-fields.js +121 -0
- package/dist/core/slot/ref.d.ts +16 -0
- package/dist/core/slot/ref.js +14 -0
- package/dist/core/slot/row.d.ts +2 -9
- package/dist/core/slot/row.js +13 -23
- package/dist/core/slot/schema-compat.d.ts +16 -0
- package/dist/core/slot/schema-compat.js +112 -0
- package/dist/core/slot/schema.d.ts +409 -76
- package/dist/core/slot/schema.js +178 -10
- package/dist/core/slot/types.d.ts +27 -92
- package/dist/core/slot/value.d.ts +26 -3
- package/dist/core/slot/value.js +89 -57
- package/dist/dev-overlay/protocol.d.ts +41 -8
- package/dist/fn-context/build-context.d.ts +3 -1
- package/dist/fn-context/build-context.js +20 -10
- package/dist/fn-context/fake/empty-api.js +9 -28
- package/dist/fn-context/fake/index.d.ts +11 -4
- package/dist/fn-context/fake/index.js +27 -2
- package/dist/fn-context/fn-params.d.ts +6 -0
- package/dist/fn-context/fn-params.js +13 -0
- package/dist/fn-context/index.d.ts +5 -5
- package/dist/fn-context/index.js +4 -4
- package/dist/fn-context/log.js +2 -2
- package/dist/fn-context/make-ctx.d.ts +11 -0
- package/dist/fn-context/make-ctx.js +18 -8
- package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +24 -0
- package/dist/fn-context/rpc/case-orchestrator-tools.js +13 -0
- package/dist/fn-context/rpc/index.d.ts +3 -1
- package/dist/fn-context/rpc/index.js +3 -1
- package/dist/fn-context/rpc/integration/index.d.ts +4 -4
- package/dist/fn-context/rpc/integration/index.js +1 -1
- package/dist/fn-context/rpc/integration/list-surfaces.d.ts +21 -0
- package/dist/fn-context/rpc/integration/{list-bindings.js → list-surfaces.js} +4 -4
- package/dist/fn-context/rpc/platform.d.ts +2 -2
- package/dist/fn-context/rpc/platform.js +1 -1
- package/dist/fn-context/rpc/records.d.ts +78 -0
- package/dist/fn-context/rpc/records.js +26 -0
- package/dist/fn-context/rpc/trpc-client.d.ts +2 -2
- package/dist/fn-context/rpc/trpc-client.js +89 -5
- package/dist/fn-context/rpc/trpc-init.d.ts +2 -2
- package/dist/fn-context/rpc/trpc-router-factory.d.ts +49 -3
- package/dist/fn-context/rpc/trpc-router-factory.js +2 -0
- package/dist/fn-context/run-scope.d.ts +4 -0
- package/dist/fn-context/run-scope.js +9 -0
- package/dist/fn-context/types.d.ts +36 -3
- package/dist/index.d.ts +37 -46
- package/dist/index.js +33 -33
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +41 -0
- package/dist/platform.d.ts +76 -0
- package/dist/platform.js +56 -0
- package/dist/voice-service/actions.d.ts +71 -4
- package/dist/voice-service/types.d.ts +42 -2
- package/dist/worker.d.ts +10 -0
- package/dist/worker.js +10 -0
- package/package.json +38 -19
- package/dist/builder/compile/compile-agenda.d.ts +0 -15
- package/dist/builder/compile/compile-agenda.js +0 -59
- package/dist/builder/merge.d.ts +0 -4
- package/dist/builder/merge.js +0 -19
- package/dist/core/context/db.d.ts +0 -69
- package/dist/core/context/db.js +0 -44
- package/dist/core/context/docusign.d.ts +0 -134
- package/dist/core/context/docusign.js +0 -108
- package/dist/core/context/filevine.d.ts +0 -323
- package/dist/core/context/filevine.js +0 -254
- package/dist/core/context/http.d.ts +0 -9
- package/dist/core/context/http.js +0 -4
- package/dist/core/context/hubspot.d.ts +0 -250
- package/dist/core/context/hubspot.js +0 -191
- package/dist/core/context/twilio.d.ts +0 -50
- package/dist/core/context/twilio.js +0 -42
- package/dist/core/functions/define/case-context.d.ts +0 -14
- package/dist/core/functions/define/case-context.js +0 -15
- package/dist/core/functions/define/compute.d.ts +0 -7
- package/dist/core/functions/define/compute.js +0 -17
- package/dist/core/functions/define/custom.d.ts +0 -4
- package/dist/core/functions/define/custom.js +0 -8
- package/dist/core/functions/define/system.d.ts +0 -7
- package/dist/core/functions/define/system.js +0 -12
- package/dist/core/slot/deps.d.ts +0 -15
- package/dist/core/slot/deps.js +0 -88
- package/dist/core/slot/derived.d.ts +0 -32
- package/dist/core/slot/derived.js +0 -115
- package/dist/core/slot/platform-session-fields.d.ts +0 -38
- package/dist/core/slot/platform-session-fields.js +0 -48
- package/dist/core/slot/sf-schema.d.ts +0 -23
- package/dist/core/slot/sf-schema.js +0 -125
- package/dist/core/slot/sf-validate.d.ts +0 -14
- package/dist/core/slot/sf-validate.js +0 -132
- package/dist/core/slot/snapshot.d.ts +0 -18
- package/dist/core/slot/snapshot.js +0 -138
- package/dist/core/slot/write.d.ts +0 -41
- package/dist/core/slot/write.js +0 -319
- package/dist/core/stage/builders.d.ts +0 -36
- package/dist/core/stage/builders.js +0 -28
- package/dist/core/stage/condition.d.ts +0 -17
- package/dist/core/stage/condition.js +0 -198
- package/dist/core/stage/drive.d.ts +0 -43
- package/dist/core/stage/drive.js +0 -77
- package/dist/core/stage/finish-when.d.ts +0 -20
- package/dist/core/stage/finish-when.js +0 -82
- package/dist/core/stage/flatten.d.ts +0 -27
- package/dist/core/stage/flatten.js +0 -464
- package/dist/core/stage/index.d.ts +0 -11
- package/dist/core/stage/index.js +0 -10
- package/dist/core/stage/simulate.d.ts +0 -24
- package/dist/core/stage/simulate.js +0 -33
- package/dist/core/stage/transfer-fields.d.ts +0 -14
- package/dist/core/stage/transfer-fields.js +0 -88
- package/dist/core/stage/types.d.ts +0 -177
- package/dist/core/stage/types.js +0 -2
- package/dist/core/stage/validate.d.ts +0 -11
- package/dist/core/stage/validate.js +0 -226
- package/dist/core/stage/walk.d.ts +0 -34
- package/dist/core/stage/walk.js +0 -96
- package/dist/fn-context/rpc/integration/list-bindings.d.ts +0 -21
- package/dist/fn-context/system-args.d.ts +0 -6
- package/dist/fn-context/system-args.js +0 -14
|
@@ -1,83 +1,37 @@
|
|
|
1
|
-
import { ROW_ID_FIELD } from './schema.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { resolveFuncName } from '../functions/func-ref.js';
|
|
5
|
-
import { buildDependencyGraph } from './deps.js';
|
|
1
|
+
import { idFieldCore, ROW_ID_FIELD } from './schema.js';
|
|
2
|
+
import { isReservedTableName } from './row.js';
|
|
3
|
+
import { compileCompanyMetadata, PLATFORM_SESSION_SCHEMA, SESSION_TABLE } from './company-metadata.js';
|
|
6
4
|
import { formatFieldPath, parseFieldPath } from './path.js';
|
|
7
|
-
function
|
|
8
|
-
return tableName === 'session' && 'rows' in table && table.rows === 'single' && 'label' in table;
|
|
9
|
-
}
|
|
10
|
-
function toRuntimeField(field, functions) {
|
|
11
|
-
if (field.derived) {
|
|
12
|
-
return {
|
|
13
|
-
derived: true,
|
|
14
|
-
label: field.label,
|
|
15
|
-
dataType: field.dataType,
|
|
16
|
-
dependsOn: field.dependsOn,
|
|
17
|
-
computeFn: resolveFuncName(field.computeFn, functions),
|
|
18
|
-
deprecated: 'deprecated' in field ? field.deprecated : undefined,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
5
|
+
function toRuntimeField(field) {
|
|
21
6
|
return {
|
|
22
|
-
derived: false,
|
|
23
7
|
label: field.label,
|
|
24
8
|
dataType: field.dataType,
|
|
25
|
-
source: field.source,
|
|
26
9
|
extraction: field.extraction,
|
|
27
|
-
|
|
28
|
-
nullable: field.nullable,
|
|
29
|
-
identifier: 'identifier' in field ? field.identifier : null,
|
|
10
|
+
options: field.options ?? null,
|
|
30
11
|
ref: 'ref' in field && field.ref ? field.ref : null,
|
|
12
|
+
...('unique' in field && field.unique ? { unique: true } : {}),
|
|
31
13
|
deprecated: 'deprecated' in field ? field.deprecated : undefined,
|
|
32
14
|
};
|
|
33
15
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
if (override.label !== undefined)
|
|
37
|
-
next.label = override.label;
|
|
38
|
-
if (override.extraction !== undefined && !next.derived) {
|
|
39
|
-
next.extraction = override.extraction;
|
|
40
|
-
}
|
|
41
|
-
return next;
|
|
42
|
-
}
|
|
43
|
-
function isValidIdField(field) {
|
|
44
|
-
return (!!field && !field.derived && field.dataType === 'string' && field.source === 'system' && field.nullable === false);
|
|
16
|
+
export function companyFieldToRuntime(field) {
|
|
17
|
+
return toRuntimeField(field);
|
|
45
18
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (!isValidIdField(existing)) {
|
|
57
|
-
issues.push({
|
|
58
|
-
code: 'invalid-id-field',
|
|
59
|
-
message: `${path} must be a non-null system string`,
|
|
60
|
-
path,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
19
|
+
/** Every row's id, the platform's: the first field of every table and of the session. */
|
|
20
|
+
const ROW_ID = { ...idFieldCore, options: null, ref: null };
|
|
21
|
+
/** The session table: one row per conversation, its row id and the company's session fields. */
|
|
22
|
+
function sessionTable(session) {
|
|
23
|
+
const fields = { [ROW_ID_FIELD]: { ...ROW_ID } };
|
|
24
|
+
for (const [fieldName, field] of Object.entries(session.fields))
|
|
25
|
+
fields[fieldName] = toRuntimeField(field);
|
|
26
|
+
return { label: 'Session', description: 'This conversation', fields };
|
|
63
27
|
}
|
|
64
28
|
function validateCompanyRefs(company, issues) {
|
|
65
29
|
for (const [tableName, table] of Object.entries(company)) {
|
|
66
30
|
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
67
|
-
if (field.derived)
|
|
68
|
-
continue;
|
|
69
31
|
if (!field.ref)
|
|
70
32
|
continue;
|
|
71
33
|
const targetTable = field.ref.table;
|
|
72
|
-
if (
|
|
73
|
-
issues.push({
|
|
74
|
-
code: 'id-cannot-ref',
|
|
75
|
-
message: `${tableName}.${ROW_ID_FIELD} cannot be a ref`,
|
|
76
|
-
path: formatFieldPath(tableName, fieldName),
|
|
77
|
-
});
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (!company[targetTable]) {
|
|
34
|
+
if (!Object.hasOwn(company, targetTable)) {
|
|
81
35
|
issues.push({
|
|
82
36
|
code: 'unknown-ref-table',
|
|
83
37
|
message: `ref table not in company schema: ${targetTable}`,
|
|
@@ -88,169 +42,37 @@ function validateCompanyRefs(company, issues) {
|
|
|
88
42
|
}
|
|
89
43
|
}
|
|
90
44
|
/**
|
|
91
|
-
*
|
|
45
|
+
* The company schema as the platform reads it (28 §2): every company table and field, and the
|
|
46
|
+
* company's session fields (`schema/session.ts`). One for the whole company: every sequence, page and
|
|
47
|
+
* function reads it; a sequence only names its `subject` (checked by `subjectTableIssues`).
|
|
92
48
|
*/
|
|
93
|
-
export function compileSlotSchema(company,
|
|
49
|
+
export function compileSlotSchema(company, options = {}) {
|
|
94
50
|
const issues = [];
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
51
|
+
const session = options.session ?? PLATFORM_SESSION_SCHEMA;
|
|
52
|
+
const companyCheck = compileCompanyMetadata(company, session);
|
|
53
|
+
if (!companyCheck.validation.ok)
|
|
54
|
+
issues.push(...companyCheck.validation.issues);
|
|
55
|
+
const tables = {};
|
|
98
56
|
for (const [tableName, companyTable] of Object.entries(company)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
issues.push({
|
|
102
|
-
code: 'reserved-table-name',
|
|
103
|
-
message: `Table name is reserved: ${tableName}`,
|
|
104
|
-
path: tableName,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
57
|
+
// Reserved table names are the company schema's to report (compileCompanyMetadata).
|
|
58
|
+
if (tableName === SESSION_TABLE || isReservedTableName(tableName))
|
|
107
59
|
continue;
|
|
108
|
-
|
|
109
|
-
const fields = {};
|
|
60
|
+
// The row id is the platform's: a schema that writes one is refused (compileCompanyMetadata).
|
|
61
|
+
const fields = { [ROW_ID_FIELD]: { ...ROW_ID } };
|
|
110
62
|
for (const [fieldName, field] of Object.entries(companyTable.fields)) {
|
|
111
|
-
|
|
63
|
+
if (fieldName !== ROW_ID_FIELD)
|
|
64
|
+
fields[fieldName] = toRuntimeField(field);
|
|
112
65
|
}
|
|
113
|
-
requireRowIdField(fields, formatFieldPath(tableName, ROW_ID_FIELD), issues);
|
|
114
66
|
tables[tableName] = {
|
|
115
67
|
label: companyTable.label,
|
|
116
|
-
rows: companyTable.rows,
|
|
117
68
|
description: companyTable.description,
|
|
118
69
|
fields,
|
|
119
70
|
};
|
|
120
71
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
continue;
|
|
124
|
-
if (isReservedTableName(tableName)) {
|
|
125
|
-
issues.push({
|
|
126
|
-
code: 'reserved-table-name',
|
|
127
|
-
message: `Table name is reserved: ${tableName}`,
|
|
128
|
-
path: tableName,
|
|
129
|
-
});
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (isSessionTable(tableName, tableView)) {
|
|
133
|
-
const fields = {};
|
|
134
|
-
for (const [fieldName, fieldDef] of Object.entries(tableView.fields)) {
|
|
135
|
-
if (fieldDef.derived) {
|
|
136
|
-
issues.push({
|
|
137
|
-
code: 'session-derived-forbidden',
|
|
138
|
-
message: 'session table must not define derived fields',
|
|
139
|
-
path: formatFieldPath('session', fieldName),
|
|
140
|
-
});
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
fields[fieldName] = toRuntimeField(fieldDef, options.functions);
|
|
144
|
-
}
|
|
145
|
-
Object.assign(fields, PLATFORM_SESSION_FIELDS);
|
|
146
|
-
requireRowIdField(fields, formatFieldPath('session', ROW_ID_FIELD), issues);
|
|
147
|
-
tables.session = {
|
|
148
|
-
label: tableView.label,
|
|
149
|
-
rows: 'single',
|
|
150
|
-
description: tableView.description,
|
|
151
|
-
fields,
|
|
152
|
-
};
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const inherited = tables[tableName];
|
|
156
|
-
if (!inherited) {
|
|
157
|
-
issues.push({
|
|
158
|
-
code: 'unknown-table',
|
|
159
|
-
message: `Sequence overrides unknown table: ${tableName}`,
|
|
160
|
-
path: tableName,
|
|
161
|
-
});
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
for (const [fieldName, override] of Object.entries(tableView.fields)) {
|
|
165
|
-
const existing = inherited.fields[fieldName];
|
|
166
|
-
if (!existing) {
|
|
167
|
-
issues.push({
|
|
168
|
-
code: 'unknown-field',
|
|
169
|
-
message: `Sequence overrides unknown field: ${tableName}.${fieldName}`,
|
|
170
|
-
path: formatFieldPath(tableName, fieldName),
|
|
171
|
-
});
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
inherited.fields[fieldName] = applyFieldOverride(existing, override);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
72
|
+
// Every sequence has the session row with all of the company's session fields.
|
|
73
|
+
tables.session = sessionTable(session);
|
|
177
74
|
validateCompanyRefs(company, issues);
|
|
178
|
-
|
|
179
|
-
for (const [tableName, table] of Object.entries(tables)) {
|
|
180
|
-
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
181
|
-
const path = formatFieldPath(tableName, fieldName);
|
|
182
|
-
if (!field.derived)
|
|
183
|
-
continue;
|
|
184
|
-
if (table.rows === 'multi') {
|
|
185
|
-
issues.push({
|
|
186
|
-
code: 'multi-derived-forbidden',
|
|
187
|
-
message: `Derived fields are not allowed on multi-row tables: ${path}`,
|
|
188
|
-
path,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
if (tableName === 'session') {
|
|
192
|
-
issues.push({
|
|
193
|
-
code: 'session-derived-forbidden',
|
|
194
|
-
message: `Derived fields are not allowed on session: ${path}`,
|
|
195
|
-
path,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
if (options.computeFns && !options.computeFns.has(field.computeFn)) {
|
|
199
|
-
issues.push({
|
|
200
|
-
code: 'missing-compute-fn',
|
|
201
|
-
message: `computeFn not found: ${field.computeFn}`,
|
|
202
|
-
path,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
for (const dep of field.dependsOn) {
|
|
206
|
-
const parsed = parseFieldPath(dep);
|
|
207
|
-
if (!parsed) {
|
|
208
|
-
issues.push({
|
|
209
|
-
code: 'invalid-depends-on',
|
|
210
|
-
message: `Invalid dependsOn path: ${dep}`,
|
|
211
|
-
path,
|
|
212
|
-
});
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const depTable = tables[parsed.table];
|
|
216
|
-
const depField = depTable?.fields[parsed.field];
|
|
217
|
-
if (!depTable || !depField) {
|
|
218
|
-
issues.push({
|
|
219
|
-
code: 'missing-depends-on',
|
|
220
|
-
message: `dependsOn refers to unknown field: ${dep}`,
|
|
221
|
-
path,
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// Enum / normal field shape checks
|
|
227
|
-
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
228
|
-
if (field.derived)
|
|
229
|
-
continue;
|
|
230
|
-
const path = formatFieldPath(tableName, fieldName);
|
|
231
|
-
if (field.enum) {
|
|
232
|
-
for (const member of field.enum) {
|
|
233
|
-
const memberType = typeof member;
|
|
234
|
-
if ((field.dataType === 'number' && memberType !== 'number') ||
|
|
235
|
-
(field.dataType !== 'number' && field.dataType !== 'boolean' && memberType !== 'string') ||
|
|
236
|
-
(field.dataType === 'boolean' && memberType !== 'boolean')) {
|
|
237
|
-
issues.push({
|
|
238
|
-
code: 'invalid-enum-member',
|
|
239
|
-
message: `enum member ${String(member)} incompatible with dataType ${field.dataType}`,
|
|
240
|
-
path,
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
const graph = buildDependencyGraph(tables);
|
|
248
|
-
issues.push(...graph.issues);
|
|
249
|
-
const schema = {
|
|
250
|
-
...tables,
|
|
251
|
-
derivedOrder: graph.derivedOrder,
|
|
252
|
-
reverseDeps: graph.reverseDeps,
|
|
253
|
-
};
|
|
75
|
+
const schema = { ...tables };
|
|
254
76
|
return {
|
|
255
77
|
schema,
|
|
256
78
|
validation: issues.length === 0 ? { ok: true } : { ok: false, issues },
|
|
@@ -280,8 +102,23 @@ export function validateFieldRefs(schema, refs) {
|
|
|
280
102
|
}
|
|
281
103
|
return issues.length === 0 ? { ok: true } : { ok: false, issues };
|
|
282
104
|
}
|
|
105
|
+
/** The ways back to `tableName`: every ref of this schema that points at it and has a `relatedName`. */
|
|
106
|
+
export function relationsOf(schema, tableName) {
|
|
107
|
+
const out = [];
|
|
108
|
+
for (const [childName, child] of compiledTableEntries(schema)) {
|
|
109
|
+
for (const [field, def] of Object.entries(child.fields)) {
|
|
110
|
+
if (def.ref?.table === tableName && def.ref.relatedName !== undefined)
|
|
111
|
+
out.push({ name: def.ref.relatedName, table: childName, field });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/** A table's own field by name; undefined for any other key, a name every object has (`toString`) included. */
|
|
117
|
+
export function getTableField(table, fieldName) {
|
|
118
|
+
return Object.hasOwn(table.fields, fieldName) ? table.fields[fieldName] : undefined;
|
|
119
|
+
}
|
|
283
120
|
export function getCompiledTable(schema, tableName) {
|
|
284
|
-
if (isReservedTableName(tableName))
|
|
121
|
+
if (isReservedTableName(tableName) || !Object.hasOwn(schema, tableName))
|
|
285
122
|
return undefined;
|
|
286
123
|
const value = schema[tableName];
|
|
287
124
|
if (!value || typeof value !== 'object' || Array.isArray(value) || !('fields' in value)) {
|
|
@@ -298,38 +135,4 @@ export function compiledTableEntries(schema) {
|
|
|
298
135
|
}
|
|
299
136
|
return out;
|
|
300
137
|
}
|
|
301
|
-
/** Snapshot / SF presentation: single → field dict, multi → row array. */
|
|
302
|
-
export function isSnapshotSingleRow(table) {
|
|
303
|
-
return table.rows === 'single';
|
|
304
|
-
}
|
|
305
|
-
/** Effective schema for Slots codegen (multi → array, single → dict). */
|
|
306
|
-
export function toEffectiveSchema(compiled) {
|
|
307
|
-
const tables = {};
|
|
308
|
-
for (const [tableName, table] of compiledTableEntries(compiled)) {
|
|
309
|
-
const fields = {};
|
|
310
|
-
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
311
|
-
fields[fieldName] = {
|
|
312
|
-
dataType: field.dataType,
|
|
313
|
-
enum: field.derived ? null : field.enum,
|
|
314
|
-
nullable: field.derived ? true : field.nullable,
|
|
315
|
-
ref: field.derived ? null : (field.ref ?? null),
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
tables[tableName] = { rows: table.rows, fields };
|
|
319
|
-
}
|
|
320
|
-
return tables;
|
|
321
|
-
}
|
|
322
|
-
export function listConversationFields(schema) {
|
|
323
|
-
const out = [];
|
|
324
|
-
for (const [tableName, table] of compiledTableEntries(schema)) {
|
|
325
|
-
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
326
|
-
if (field.derived)
|
|
327
|
-
continue;
|
|
328
|
-
if (field.source !== 'conversation')
|
|
329
|
-
continue;
|
|
330
|
-
out.push(formatFieldPath(tableName, fieldName));
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return out;
|
|
334
|
-
}
|
|
335
138
|
//# sourceMappingURL=compile-schema.js.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A contact's timezone when its `timezone` field is empty (25 §2.2): from its postal code, then its
|
|
3
|
+
* state, then its phone's area code. Read only: nothing is written back to `timezone`, which stays what
|
|
4
|
+
* the contact said (its extraction forbids inferring it from a phone number).
|
|
5
|
+
*
|
|
6
|
+
* The tables cover the US (with its territories) and Canada, the platform's numbers. A state in two
|
|
7
|
+
* zones gets its most populous one; the postal-code and area-code exceptions below fix the parts that
|
|
8
|
+
* differ (the Florida panhandle, El Paso, east Tennessee, north Idaho…).
|
|
9
|
+
*/
|
|
10
|
+
/** US states, territories and DC, and Canadian provinces and territories: their main IANA zone. */
|
|
11
|
+
export declare const STATE_TIMEZONES: {
|
|
12
|
+
readonly AL: 'America/Chicago';
|
|
13
|
+
readonly AK: 'America/Anchorage';
|
|
14
|
+
readonly AZ: 'America/Phoenix';
|
|
15
|
+
readonly AR: 'America/Chicago';
|
|
16
|
+
readonly CA: 'America/Los_Angeles';
|
|
17
|
+
readonly CO: 'America/Denver';
|
|
18
|
+
readonly CT: 'America/New_York';
|
|
19
|
+
readonly DE: 'America/New_York';
|
|
20
|
+
readonly DC: 'America/New_York';
|
|
21
|
+
readonly FL: 'America/New_York';
|
|
22
|
+
readonly GA: 'America/New_York';
|
|
23
|
+
readonly HI: 'Pacific/Honolulu';
|
|
24
|
+
readonly ID: 'America/Boise';
|
|
25
|
+
readonly IL: 'America/Chicago';
|
|
26
|
+
readonly IN: 'America/Indiana/Indianapolis';
|
|
27
|
+
readonly IA: 'America/Chicago';
|
|
28
|
+
readonly KS: 'America/Chicago';
|
|
29
|
+
readonly KY: 'America/New_York';
|
|
30
|
+
readonly LA: 'America/Chicago';
|
|
31
|
+
readonly ME: 'America/New_York';
|
|
32
|
+
readonly MD: 'America/New_York';
|
|
33
|
+
readonly MA: 'America/New_York';
|
|
34
|
+
readonly MI: 'America/Detroit';
|
|
35
|
+
readonly MN: 'America/Chicago';
|
|
36
|
+
readonly MS: 'America/Chicago';
|
|
37
|
+
readonly MO: 'America/Chicago';
|
|
38
|
+
readonly MT: 'America/Denver';
|
|
39
|
+
readonly NE: 'America/Chicago';
|
|
40
|
+
readonly NV: 'America/Los_Angeles';
|
|
41
|
+
readonly NH: 'America/New_York';
|
|
42
|
+
readonly NJ: 'America/New_York';
|
|
43
|
+
readonly NM: 'America/Denver';
|
|
44
|
+
readonly NY: 'America/New_York';
|
|
45
|
+
readonly NC: 'America/New_York';
|
|
46
|
+
readonly ND: 'America/Chicago';
|
|
47
|
+
readonly OH: 'America/New_York';
|
|
48
|
+
readonly OK: 'America/Chicago';
|
|
49
|
+
readonly OR: 'America/Los_Angeles';
|
|
50
|
+
readonly PA: 'America/New_York';
|
|
51
|
+
readonly RI: 'America/New_York';
|
|
52
|
+
readonly SC: 'America/New_York';
|
|
53
|
+
readonly SD: 'America/Chicago';
|
|
54
|
+
readonly TN: 'America/Chicago';
|
|
55
|
+
readonly TX: 'America/Chicago';
|
|
56
|
+
readonly UT: 'America/Denver';
|
|
57
|
+
readonly VT: 'America/New_York';
|
|
58
|
+
readonly VA: 'America/New_York';
|
|
59
|
+
readonly WA: 'America/Los_Angeles';
|
|
60
|
+
readonly WV: 'America/New_York';
|
|
61
|
+
readonly WI: 'America/Chicago';
|
|
62
|
+
readonly WY: 'America/Denver';
|
|
63
|
+
readonly PR: 'America/Puerto_Rico';
|
|
64
|
+
readonly VI: 'America/St_Thomas';
|
|
65
|
+
readonly GU: 'Pacific/Guam';
|
|
66
|
+
readonly AS: 'Pacific/Pago_Pago';
|
|
67
|
+
readonly MP: 'Pacific/Saipan';
|
|
68
|
+
readonly AB: 'America/Edmonton';
|
|
69
|
+
readonly BC: 'America/Vancouver';
|
|
70
|
+
readonly MB: 'America/Winnipeg';
|
|
71
|
+
readonly NB: 'America/Moncton';
|
|
72
|
+
readonly NL: 'America/St_Johns';
|
|
73
|
+
readonly NS: 'America/Halifax';
|
|
74
|
+
readonly NT: 'America/Yellowknife';
|
|
75
|
+
readonly NU: 'America/Iqaluit';
|
|
76
|
+
readonly ON: 'America/Toronto';
|
|
77
|
+
readonly PE: 'America/Halifax';
|
|
78
|
+
readonly QC: 'America/Toronto';
|
|
79
|
+
readonly SK: 'America/Regina';
|
|
80
|
+
readonly YT: 'America/Whitehorse';
|
|
81
|
+
};
|
|
82
|
+
/** A two-letter US state, territory, DC or Canadian province code. */
|
|
83
|
+
export type StateCode = keyof typeof STATE_TIMEZONES;
|
|
84
|
+
/** The codes `state` takes (the platform field's allowed values). */
|
|
85
|
+
export declare const STATE_CODES: StateCode[];
|
|
86
|
+
/** `value` when it names a zone this runtime knows, else null. */
|
|
87
|
+
export declare function knownTimezone(value: unknown): string | null;
|
|
88
|
+
/** A two-letter state or province code (any case), else null. */
|
|
89
|
+
export declare function stateCode(value: unknown): StateCode | null;
|
|
90
|
+
/** The zone of a US ZIP (5 digits or ZIP+4) or a Canadian postal code; null when not one. */
|
|
91
|
+
export declare function postalTimezone(value: unknown): string | null;
|
|
92
|
+
/** The zone of a North American number's area code (E.164 `+1NXX…`); null for any other. */
|
|
93
|
+
export declare function areaCodeTimezone(phone: unknown): string | null;
|
|
94
|
+
export type TimezoneSource = 'timezone' | 'postal_code' | 'state' | 'phone' | 'default';
|
|
95
|
+
/**
|
|
96
|
+
* The contact's zone and where it came from: its own `timezone`, else its postal code, its state, its
|
|
97
|
+
* phone's area code, and last `fallback`. A contact in another country (`country` set, not the US or
|
|
98
|
+
* Canada) skips the postal code and state: a 5-digit code abroad is not a ZIP.
|
|
99
|
+
*/
|
|
100
|
+
export declare function contactTimezone(fields: {
|
|
101
|
+
timezone?: unknown;
|
|
102
|
+
postal_code?: unknown;
|
|
103
|
+
state?: unknown;
|
|
104
|
+
country?: unknown;
|
|
105
|
+
phone?: unknown;
|
|
106
|
+
}, fallback: string): {
|
|
107
|
+
timezone: string;
|
|
108
|
+
source: TimezoneSource;
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=contact-timezone.d.ts.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A contact's timezone when its `timezone` field is empty (25 §2.2): from its postal code, then its
|
|
3
|
+
* state, then its phone's area code. Read only: nothing is written back to `timezone`, which stays what
|
|
4
|
+
* the contact said (its extraction forbids inferring it from a phone number).
|
|
5
|
+
*
|
|
6
|
+
* The tables cover the US (with its territories) and Canada, the platform's numbers. A state in two
|
|
7
|
+
* zones gets its most populous one; the postal-code and area-code exceptions below fix the parts that
|
|
8
|
+
* differ (the Florida panhandle, El Paso, east Tennessee, north Idaho…).
|
|
9
|
+
*/
|
|
10
|
+
/** US states, territories and DC, and Canadian provinces and territories: their main IANA zone. */
|
|
11
|
+
export const STATE_TIMEZONES = {
|
|
12
|
+
AL: 'America/Chicago', AK: 'America/Anchorage', AZ: 'America/Phoenix', AR: 'America/Chicago',
|
|
13
|
+
CA: 'America/Los_Angeles', CO: 'America/Denver', CT: 'America/New_York', DE: 'America/New_York',
|
|
14
|
+
DC: 'America/New_York', FL: 'America/New_York', GA: 'America/New_York', HI: 'Pacific/Honolulu',
|
|
15
|
+
ID: 'America/Boise', IL: 'America/Chicago', IN: 'America/Indiana/Indianapolis', IA: 'America/Chicago',
|
|
16
|
+
KS: 'America/Chicago', KY: 'America/New_York', LA: 'America/Chicago', ME: 'America/New_York',
|
|
17
|
+
MD: 'America/New_York', MA: 'America/New_York', MI: 'America/Detroit', MN: 'America/Chicago',
|
|
18
|
+
MS: 'America/Chicago', MO: 'America/Chicago', MT: 'America/Denver', NE: 'America/Chicago',
|
|
19
|
+
NV: 'America/Los_Angeles', NH: 'America/New_York', NJ: 'America/New_York', NM: 'America/Denver',
|
|
20
|
+
NY: 'America/New_York', NC: 'America/New_York', ND: 'America/Chicago', OH: 'America/New_York',
|
|
21
|
+
OK: 'America/Chicago', OR: 'America/Los_Angeles', PA: 'America/New_York', RI: 'America/New_York',
|
|
22
|
+
SC: 'America/New_York', SD: 'America/Chicago', TN: 'America/Chicago', TX: 'America/Chicago',
|
|
23
|
+
UT: 'America/Denver', VT: 'America/New_York', VA: 'America/New_York', WA: 'America/Los_Angeles',
|
|
24
|
+
WV: 'America/New_York', WI: 'America/Chicago', WY: 'America/Denver',
|
|
25
|
+
PR: 'America/Puerto_Rico', VI: 'America/St_Thomas', GU: 'Pacific/Guam', AS: 'Pacific/Pago_Pago', MP: 'Pacific/Saipan',
|
|
26
|
+
AB: 'America/Edmonton', BC: 'America/Vancouver', MB: 'America/Winnipeg', NB: 'America/Moncton',
|
|
27
|
+
NL: 'America/St_Johns', NS: 'America/Halifax', NT: 'America/Yellowknife', NU: 'America/Iqaluit',
|
|
28
|
+
ON: 'America/Toronto', PE: 'America/Halifax', QC: 'America/Toronto', SK: 'America/Regina', YT: 'America/Whitehorse',
|
|
29
|
+
};
|
|
30
|
+
/** The codes `state` takes (the platform field's allowed values). */
|
|
31
|
+
export const STATE_CODES = Object.keys(STATE_TIMEZONES);
|
|
32
|
+
/** US ZIP code prefixes (first three digits), as inclusive ranges, by state (USPS). */
|
|
33
|
+
const ZIP3_RANGES = [
|
|
34
|
+
[5, 5, 'NY'], [6, 7, 'PR'], [8, 8, 'VI'], [9, 9, 'PR'], [10, 27, 'MA'], [28, 29, 'RI'], [30, 38, 'NH'],
|
|
35
|
+
[39, 49, 'ME'], [50, 59, 'VT'], [60, 69, 'CT'], [70, 89, 'NJ'], [100, 149, 'NY'], [150, 196, 'PA'],
|
|
36
|
+
[197, 199, 'DE'], [200, 205, 'DC'], [206, 219, 'MD'], [220, 246, 'VA'], [247, 268, 'WV'], [270, 289, 'NC'],
|
|
37
|
+
[290, 299, 'SC'], [300, 319, 'GA'], [320, 349, 'FL'], [350, 369, 'AL'], [370, 385, 'TN'], [386, 397, 'MS'],
|
|
38
|
+
[398, 399, 'GA'], [400, 427, 'KY'], [430, 459, 'OH'], [460, 479, 'IN'], [480, 499, 'MI'], [500, 528, 'IA'],
|
|
39
|
+
[530, 549, 'WI'], [550, 567, 'MN'], [569, 569, 'DC'], [570, 577, 'SD'], [580, 588, 'ND'], [590, 599, 'MT'],
|
|
40
|
+
[600, 629, 'IL'], [630, 658, 'MO'], [660, 679, 'KS'], [680, 693, 'NE'], [700, 714, 'LA'], [716, 729, 'AR'],
|
|
41
|
+
[730, 749, 'OK'], [750, 799, 'TX'], [800, 816, 'CO'], [820, 831, 'WY'], [832, 838, 'ID'], [840, 847, 'UT'],
|
|
42
|
+
[850, 865, 'AZ'], [870, 884, 'NM'], [885, 885, 'TX'], [889, 898, 'NV'], [900, 961, 'CA'], [967, 968, 'HI'],
|
|
43
|
+
[969, 969, 'GU'], [970, 979, 'OR'], [980, 994, 'WA'], [995, 999, 'AK'],
|
|
44
|
+
];
|
|
45
|
+
/** ZIP prefixes in another zone than their state's main one. */
|
|
46
|
+
const ZIP3_TIMEZONES = {
|
|
47
|
+
324: 'America/Chicago', 325: 'America/Chicago', // Florida panhandle west of the Apalachicola
|
|
48
|
+
373: 'America/New_York', 374: 'America/New_York', 376: 'America/New_York', // east Tennessee
|
|
49
|
+
377: 'America/New_York', 378: 'America/New_York', 379: 'America/New_York',
|
|
50
|
+
420: 'America/Chicago', 421: 'America/Chicago', 422: 'America/Chicago', 423: 'America/Chicago', 424: 'America/Chicago', // west Kentucky
|
|
51
|
+
463: 'America/Chicago', 464: 'America/Chicago', 476: 'America/Chicago', 477: 'America/Chicago', // northwest and southwest Indiana
|
|
52
|
+
577: 'America/Denver', // west South Dakota (Rapid City)
|
|
53
|
+
693: 'America/Denver', // Nebraska panhandle
|
|
54
|
+
798: 'America/Denver', 799: 'America/Denver', 885: 'America/Denver', // El Paso
|
|
55
|
+
835: 'America/Los_Angeles', 838: 'America/Los_Angeles', // north Idaho
|
|
56
|
+
979: 'America/Boise', // east Oregon (Ontario)
|
|
57
|
+
};
|
|
58
|
+
/** Canadian postal codes: the first letter's province. */
|
|
59
|
+
const CANADA_POSTAL = {
|
|
60
|
+
A: 'NL', B: 'NS', C: 'PE', E: 'NB', G: 'QC', H: 'QC', J: 'QC', K: 'ON', L: 'ON', M: 'ON', N: 'ON', P: 'ON',
|
|
61
|
+
R: 'MB', S: 'SK', T: 'AB', V: 'BC', X: 'NT', Y: 'YT',
|
|
62
|
+
};
|
|
63
|
+
/** North American area codes by state or province (geographic ones; toll-free and non-geographic are left out). */
|
|
64
|
+
const AREA_CODES = byCode({
|
|
65
|
+
AL: '205 251 256 334 659 938', AK: '907', AZ: '480 520 602 623 928', AR: '327 479 501 870',
|
|
66
|
+
CA: '209 213 279 310 323 341 350 369 408 415 424 442 510 530 559 562 619 626 628 650 657 661 669 707 714 747 760 805 818 820 831 840 858 909 916 925 949 951',
|
|
67
|
+
CO: '303 719 720 970 983', CT: '203 475 860 959', DE: '302', DC: '202 771',
|
|
68
|
+
FL: '239 305 321 324 352 386 407 448 561 645 656 689 727 728 754 772 786 813 850 863 904 941 954',
|
|
69
|
+
GA: '229 404 470 478 678 706 762 770 912 943', HI: '808', ID: '208 986',
|
|
70
|
+
IL: '217 224 309 312 331 447 464 618 630 708 730 773 779 815 847 861 872', IN: '219 260 317 463 574 765 812 930',
|
|
71
|
+
IA: '319 515 563 641 712', KS: '316 620 785 913', KY: '270 364 502 606 859', LA: '225 318 337 504 985', ME: '207',
|
|
72
|
+
MD: '227 240 301 410 443 667', MA: '339 351 413 508 617 774 781 857 978',
|
|
73
|
+
MI: '231 248 269 313 517 586 616 679 734 810 906 947 989', MN: '218 320 507 612 651 763 924 952',
|
|
74
|
+
MS: '228 471 601 662 769', MO: '235 314 417 557 573 636 660 816 975', MT: '406', NE: '308 402 531',
|
|
75
|
+
NV: '702 725 775', NH: '603', NJ: '201 551 609 640 732 848 856 862 908 973', NM: '505 575',
|
|
76
|
+
NY: '212 315 329 332 347 363 516 518 585 607 624 631 646 680 716 718 838 845 914 917 929 934',
|
|
77
|
+
NC: '252 336 472 704 743 828 910 919 980 984', ND: '701',
|
|
78
|
+
OH: '216 220 234 283 326 330 380 419 436 440 513 567 614 740 937', OK: '405 539 572 580 918', OR: '458 503 541 971',
|
|
79
|
+
PA: '215 223 267 272 412 445 484 570 582 610 717 724 814 835 878', RI: '401', SC: '803 821 839 843 854 864', SD: '605',
|
|
80
|
+
TN: '423 615 629 731 865 901 931',
|
|
81
|
+
TX: '210 214 254 281 325 346 361 409 430 432 469 512 682 713 726 737 806 817 830 832 903 915 936 940 945 956 972 979',
|
|
82
|
+
UT: '385 435 801', VT: '802', VA: '276 434 540 571 686 703 757 804 826 948', WA: '206 253 360 425 509 564',
|
|
83
|
+
WV: '304 681', WI: '262 274 353 414 534 608 715 920', WY: '307',
|
|
84
|
+
PR: '787 939', VI: '340', GU: '671', AS: '684', MP: '670',
|
|
85
|
+
AB: '368 403 587 780 825', BC: '236 250 257 604 672 778', MB: '204 431 584', NB: '428 506', NL: '709 879', NS: '782 902',
|
|
86
|
+
ON: '226 249 289 343 365 382 387 416 437 519 548 613 647 683 705 742 753 807 905 942',
|
|
87
|
+
QC: '263 354 367 418 438 450 468 514 579 581 819 873', SK: '306 474 639', NT: '867',
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* Area codes in another zone than their state's main one. An area code that spans two zones keeps
|
|
91
|
+
* the eastern one: hours that end in the evening (Florida's 20:00) then still hold on the western
|
|
92
|
+
* side, an hour earlier there. So 850 / 448 (Tallahassee is Eastern, Pensacola Central) and 270 / 364
|
|
93
|
+
* (Elizabethtown is Eastern, Bowling Green Central) stay with their state's Eastern zone.
|
|
94
|
+
*/
|
|
95
|
+
const AREA_CODE_TIMEZONES = {
|
|
96
|
+
'915': 'America/Denver', // El Paso
|
|
97
|
+
'423': 'America/New_York', '865': 'America/New_York', // east Tennessee
|
|
98
|
+
'219': 'America/Chicago', // northwest Indiana
|
|
99
|
+
};
|
|
100
|
+
function byCode(lists) {
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const [state, codes] of Object.entries(lists))
|
|
103
|
+
for (const code of codes.split(' '))
|
|
104
|
+
out[code] = state;
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
/** `value` when it names a zone this runtime knows, else null. */
|
|
108
|
+
export function knownTimezone(value) {
|
|
109
|
+
if (typeof value !== 'string' || !value.trim())
|
|
110
|
+
return null;
|
|
111
|
+
try {
|
|
112
|
+
new Intl.DateTimeFormat('en-US', { timeZone: value.trim() }).format();
|
|
113
|
+
return value.trim();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** A two-letter state or province code (any case), else null. */
|
|
120
|
+
export function stateCode(value) {
|
|
121
|
+
if (typeof value !== 'string')
|
|
122
|
+
return null;
|
|
123
|
+
const code = value.trim().toUpperCase();
|
|
124
|
+
return Object.hasOwn(STATE_TIMEZONES, code) ? code : null;
|
|
125
|
+
}
|
|
126
|
+
/** The zone of a US ZIP (5 digits or ZIP+4) or a Canadian postal code; null when not one. */
|
|
127
|
+
export function postalTimezone(value) {
|
|
128
|
+
if (typeof value !== 'string')
|
|
129
|
+
return null;
|
|
130
|
+
const text = value.trim().toUpperCase();
|
|
131
|
+
const zip = /^(\d{5})(?:-?\d{4})?$/.exec(text);
|
|
132
|
+
if (zip) {
|
|
133
|
+
const prefix = Number(zip[1].slice(0, 3));
|
|
134
|
+
if (ZIP3_TIMEZONES[prefix])
|
|
135
|
+
return ZIP3_TIMEZONES[prefix];
|
|
136
|
+
const range = ZIP3_RANGES.find(([lo, hi]) => prefix >= lo && prefix <= hi);
|
|
137
|
+
return range ? STATE_TIMEZONES[range[2]] : null;
|
|
138
|
+
}
|
|
139
|
+
if (/^[A-Z]\d[A-Z] ?\d[A-Z]\d$/.test(text)) {
|
|
140
|
+
const province = CANADA_POSTAL[text[0]];
|
|
141
|
+
return province ? STATE_TIMEZONES[province] : null;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/** The zone of a North American number's area code (E.164 `+1NXX…`); null for any other. */
|
|
146
|
+
export function areaCodeTimezone(phone) {
|
|
147
|
+
if (typeof phone !== 'string')
|
|
148
|
+
return null;
|
|
149
|
+
const match = /^\+1(\d{3})\d{7}$/.exec(phone.replace(/;ext=.*$/, ''));
|
|
150
|
+
if (!match)
|
|
151
|
+
return null;
|
|
152
|
+
const code = match[1];
|
|
153
|
+
if (AREA_CODE_TIMEZONES[code])
|
|
154
|
+
return AREA_CODE_TIMEZONES[code];
|
|
155
|
+
const state = AREA_CODES[code];
|
|
156
|
+
return state ? STATE_TIMEZONES[state] : null;
|
|
157
|
+
}
|
|
158
|
+
/** A country outside the US (and its territories) and Canada, whose postal codes and states the tables do not cover. */
|
|
159
|
+
function foreignCountry(country) {
|
|
160
|
+
if (typeof country !== 'string' || country.trim() === '')
|
|
161
|
+
return false;
|
|
162
|
+
return !/^(US|USA|UNITED STATES|CA|CAN|CANADA|PR|GU|VI|AS|MP)$/.test(country.trim().toUpperCase());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The contact's zone and where it came from: its own `timezone`, else its postal code, its state, its
|
|
166
|
+
* phone's area code, and last `fallback`. A contact in another country (`country` set, not the US or
|
|
167
|
+
* Canada) skips the postal code and state: a 5-digit code abroad is not a ZIP.
|
|
168
|
+
*/
|
|
169
|
+
export function contactTimezone(fields, fallback) {
|
|
170
|
+
const own = knownTimezone(fields.timezone);
|
|
171
|
+
if (own)
|
|
172
|
+
return { timezone: own, source: 'timezone' };
|
|
173
|
+
if (!foreignCountry(fields.country)) {
|
|
174
|
+
const postal = postalTimezone(fields.postal_code);
|
|
175
|
+
if (postal)
|
|
176
|
+
return { timezone: postal, source: 'postal_code' };
|
|
177
|
+
const state = stateCode(fields.state);
|
|
178
|
+
if (state)
|
|
179
|
+
return { timezone: STATE_TIMEZONES[state], source: 'state' };
|
|
180
|
+
}
|
|
181
|
+
const area = areaCodeTimezone(fields.phone);
|
|
182
|
+
if (area)
|
|
183
|
+
return { timezone: area, source: 'phone' };
|
|
184
|
+
return { timezone: fallback, source: 'default' };
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=contact-timezone.js.map
|