@comms-id/asx 0.4.2 → 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.
- package/README.md +1 -1
- package/dist/client-replies.d.ts +0 -17
- package/dist/client-replies.js +0 -3
- package/dist/company-record.js +0 -2
- package/dist/descriptor.d.ts +2 -2
- package/dist/descriptor.js +2 -2
- package/dist/generated/fixtures.js +2 -2
- package/dist/generated/operations.js +23 -4
- package/dist/generated/types.d.ts +2 -1
- package/dist/refresh.js +0 -4
- package/dist/snapshot.d.ts +0 -33
- package/dist/snapshot.js +0 -20
- package/package.json +1 -1
- package/dist/contract/http-api.d.ts +0 -139
- package/dist/contract/http-api.js +0 -100
- package/dist/profile-contact.d.ts +0 -13
- package/dist/profile-contact.js +0 -66
- package/dist/requests.d.ts +0 -10
- package/dist/requests.js +0 -21
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Typed client for the Comms.ID ASX API (`https://api.comms.id/asx/v1`): ASX-listed companies: suggest as a user types, and look one up.
|
|
4
4
|
|
|
5
|
-
`src/` is generated from the ASX contract (`packages/asx-service/openapi/asx.
|
|
5
|
+
`src/` is generated from the ASX contract (`packages/asx-service/openapi/asx.v2.json`) by `@comms-id/forge-transformers`. Do not edit it: change the contract and run `pnpm --filter @comms-id/forge-transformers generate`.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { createAsxClient } from "@comms-id/asx/server";
|
package/dist/client-replies.d.ts
CHANGED
|
@@ -157,22 +157,5 @@ export declare const AbnMatchesResponse: Schema.Struct<{
|
|
|
157
157
|
}>>;
|
|
158
158
|
}>;
|
|
159
159
|
}>>;
|
|
160
|
-
/** Name-rule matches (#385): a lead for the user to confirm, not issuer proof. */
|
|
161
|
-
readonly nameMatches: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
162
|
-
readonly directory: Schema.Struct<{
|
|
163
|
-
readonly ticker: Schema.String;
|
|
164
|
-
readonly companyName: Schema.String;
|
|
165
|
-
readonly industryGroup: Schema.NullOr<Schema.String>;
|
|
166
|
-
readonly listingDate: Schema.NullOr<Schema.String>;
|
|
167
|
-
readonly marketCapitalisation: Schema.NullOr<Schema.Number>;
|
|
168
|
-
}>;
|
|
169
|
-
readonly nameMatch: Schema.Struct<{
|
|
170
|
-
readonly abn: Schema.String;
|
|
171
|
-
readonly acn: Schema.NullOr<Schema.String>;
|
|
172
|
-
readonly abnEntityName: Schema.NonEmptyString;
|
|
173
|
-
readonly rule: Schema.Literals<readonly ["full-name", "suffix-removed", "the-moved", "comma-ignored"]>;
|
|
174
|
-
readonly abnSnapshotDate: Schema.String;
|
|
175
|
-
}>;
|
|
176
|
-
}>>>;
|
|
177
160
|
}>;
|
|
178
161
|
export type AbnMatchesReply = typeof AbnMatchesResponse.Type;
|
package/dist/client-replies.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { ConfirmedEnrichment } from "./enrichment.js";
|
|
3
3
|
import { DirectoryEntry, Failure, Observation, Timestamp } from "./model.js";
|
|
4
|
-
import { AbnNameMatch } from "./snapshot.js";
|
|
5
4
|
const Generation = Schema.String.check(Schema.isPattern(/^[a-f0-9]{64}$/));
|
|
6
5
|
export const LookupResponse = Schema.Union([
|
|
7
6
|
Schema.Struct({
|
|
@@ -52,6 +51,4 @@ export const SuggestionsResponse = Schema.Union([
|
|
|
52
51
|
export const AbnMatchesResponse = Schema.Struct({
|
|
53
52
|
...Saved,
|
|
54
53
|
matches: Schema.Array(Schema.Struct({ directory: DirectoryEntry, enrichment: ConfirmedEnrichment })).check(Schema.isMaxLength(20_000)),
|
|
55
|
-
/** Name-rule matches (#385): a lead for the user to confirm, not issuer proof. */
|
|
56
|
-
nameMatches: Schema.optionalKey(Schema.Array(Schema.Struct({ directory: DirectoryEntry, nameMatch: AbnNameMatch })).check(Schema.isMaxLength(20))),
|
|
57
54
|
});
|
package/dist/company-record.js
CHANGED
|
@@ -25,7 +25,6 @@ export function mergeCompanyRecord(record, incoming) {
|
|
|
25
25
|
sources,
|
|
26
26
|
observation: null,
|
|
27
27
|
missingAt: { startedAt: asx.startedAt, observedAt: asx.observedAt },
|
|
28
|
-
...(record.abnNameMatch ? { abnNameMatch: structuredClone(record.abnNameMatch) } : {}),
|
|
29
28
|
};
|
|
30
29
|
const company = parseCompany(ticker, asx.payload);
|
|
31
30
|
if (company.sourceId !== asx.sourceId)
|
|
@@ -55,6 +54,5 @@ export function mergeCompanyRecord(record, incoming) {
|
|
|
55
54
|
...(record.enrichment && validEnrichment(record.enrichment, company)
|
|
56
55
|
? { enrichment: structuredClone(record.enrichment) }
|
|
57
56
|
: {}),
|
|
58
|
-
...(record.abnNameMatch ? { abnNameMatch: structuredClone(record.abnNameMatch) } : {}),
|
|
59
57
|
};
|
|
60
58
|
}
|
package/dist/descriptor.d.ts
CHANGED
|
@@ -6,10 +6,10 @@ export declare const asxProduct: {
|
|
|
6
6
|
readonly operations: readonly [{
|
|
7
7
|
readonly name: "lookup";
|
|
8
8
|
readonly method: "POST";
|
|
9
|
-
readonly path: "/asx/
|
|
9
|
+
readonly path: "/asx/v2/lookup";
|
|
10
10
|
}, {
|
|
11
11
|
readonly name: "suggest";
|
|
12
12
|
readonly method: "POST";
|
|
13
|
-
readonly path: "/asx/
|
|
13
|
+
readonly path: "/asx/v2/suggest";
|
|
14
14
|
}];
|
|
15
15
|
};
|
package/dist/descriptor.js
CHANGED
|
@@ -9,12 +9,12 @@ export const asxProduct = {
|
|
|
9
9
|
{
|
|
10
10
|
"name": "lookup",
|
|
11
11
|
"method": "POST",
|
|
12
|
-
"path": "/asx/
|
|
12
|
+
"path": "/asx/v2/lookup"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"name": "suggest",
|
|
16
16
|
"method": "POST",
|
|
17
|
-
"path": "/asx/
|
|
17
|
+
"path": "/asx/v2/suggest"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
20
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Every reply is the example in the contract.
|
|
3
3
|
/** LOCAL FIXTURES: one canned reply for each operation path. Fake data. */
|
|
4
4
|
export const fixtures = {
|
|
5
|
-
"/asx/
|
|
5
|
+
"/asx/v2/lookup": {
|
|
6
6
|
"status": "found",
|
|
7
7
|
"source": "live",
|
|
8
8
|
"observation": {
|
|
@@ -45,7 +45,7 @@ export const fixtures = {
|
|
|
45
45
|
"lastChangedAt": "2026-10-03T01:29:55.678Z"
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
|
-
"/asx/
|
|
48
|
+
"/asx/v2/suggest": {
|
|
49
49
|
"source": "live",
|
|
50
50
|
"directoryObservedAt": "2026-10-03T00:00:00.000Z",
|
|
51
51
|
"suggestions": []
|
|
@@ -4,7 +4,7 @@ export const operations = {
|
|
|
4
4
|
lookup: {
|
|
5
5
|
name: "lookup",
|
|
6
6
|
method: "POST",
|
|
7
|
-
path: "/asx/
|
|
7
|
+
path: "/asx/v2/lookup",
|
|
8
8
|
safeToRetry: true,
|
|
9
9
|
request: {
|
|
10
10
|
"t": "obj",
|
|
@@ -452,7 +452,7 @@ export const operations = {
|
|
|
452
452
|
"abn": {
|
|
453
453
|
"t": "str"
|
|
454
454
|
},
|
|
455
|
-
"
|
|
455
|
+
"asicNumber": {
|
|
456
456
|
"t": "any_of",
|
|
457
457
|
"of": [
|
|
458
458
|
{
|
|
@@ -464,6 +464,24 @@ export const operations = {
|
|
|
464
464
|
}
|
|
465
465
|
]
|
|
466
466
|
},
|
|
467
|
+
"asicNumberType": {
|
|
468
|
+
"t": "any_of",
|
|
469
|
+
"of": [
|
|
470
|
+
{
|
|
471
|
+
"t": "str",
|
|
472
|
+
"values": [
|
|
473
|
+
"ACN",
|
|
474
|
+
"ARBN",
|
|
475
|
+
"ARSN",
|
|
476
|
+
"ARFN",
|
|
477
|
+
"undetermined"
|
|
478
|
+
]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"t": "null"
|
|
482
|
+
}
|
|
483
|
+
]
|
|
484
|
+
},
|
|
467
485
|
"legalName": {
|
|
468
486
|
"t": "str",
|
|
469
487
|
"min": 1
|
|
@@ -575,7 +593,8 @@ export const operations = {
|
|
|
575
593
|
"ticker",
|
|
576
594
|
"sourceId",
|
|
577
595
|
"abn",
|
|
578
|
-
"
|
|
596
|
+
"asicNumber",
|
|
597
|
+
"asicNumberType",
|
|
579
598
|
"legalName",
|
|
580
599
|
"identityEvidence",
|
|
581
600
|
"abrEvidence",
|
|
@@ -650,7 +669,7 @@ export const operations = {
|
|
|
650
669
|
suggest: {
|
|
651
670
|
name: "suggest",
|
|
652
671
|
method: "POST",
|
|
653
|
-
path: "/asx/
|
|
672
|
+
path: "/asx/v2/suggest",
|
|
654
673
|
safeToRetry: true,
|
|
655
674
|
request: {
|
|
656
675
|
"t": "obj",
|
|
@@ -70,7 +70,8 @@ export type LookupResponse = {
|
|
|
70
70
|
readonly ticker: string;
|
|
71
71
|
readonly sourceId: string;
|
|
72
72
|
readonly abn: string;
|
|
73
|
-
readonly
|
|
73
|
+
readonly asicNumber: string | null;
|
|
74
|
+
readonly asicNumberType: "ACN" | "ARBN" | "ARSN" | "ARFN" | "undetermined" | null;
|
|
74
75
|
readonly legalName: string;
|
|
75
76
|
readonly identityEvidence: {
|
|
76
77
|
readonly url: string;
|
package/dist/refresh.js
CHANGED
|
@@ -140,10 +140,6 @@ export function applyRefresh(previousInput, directoryInput, attemptsInput) {
|
|
|
140
140
|
: {}),
|
|
141
141
|
...(missingAt ? { missingAt } : {}),
|
|
142
142
|
...(enrichment ? { enrichment } : {}),
|
|
143
|
-
// A name match belongs to the directory name it was made for (#385).
|
|
144
|
-
...(prior?.abnNameMatch && prior.directory.companyName === entry.companyName
|
|
145
|
-
? { abnNameMatch: prior.abnNameMatch }
|
|
146
|
-
: {}),
|
|
147
143
|
};
|
|
148
144
|
});
|
|
149
145
|
return {
|
package/dist/snapshot.d.ts
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { DirectoryEntry } from "./model.js";
|
|
3
3
|
import { ConfirmedEnrichment } from "./enrichment.js";
|
|
4
|
-
/**
|
|
5
|
-
* An ABN matched to a listed company by the owner-accepted name rules (#385): the
|
|
6
|
-
* full legal name, one trailing company suffix removed, a trailing "(THE)" moved to
|
|
7
|
-
* the front, or a comma ignored. A name match, never proof of issuer identity.
|
|
8
|
-
*/
|
|
9
|
-
export declare const AbnNameMatch: Schema.Struct<{
|
|
10
|
-
readonly abn: Schema.String;
|
|
11
|
-
readonly acn: Schema.NullOr<Schema.String>;
|
|
12
|
-
readonly abnEntityName: Schema.NonEmptyString;
|
|
13
|
-
readonly rule: Schema.Literals<readonly ["full-name", "suffix-removed", "the-moved", "comma-ignored"]>;
|
|
14
|
-
readonly abnSnapshotDate: Schema.String;
|
|
15
|
-
}>;
|
|
16
|
-
export interface AbnNameMatch extends Schema.Schema.Type<typeof AbnNameMatch> {
|
|
17
|
-
}
|
|
18
4
|
export declare const CompanyRecord: Schema.Struct<{
|
|
19
5
|
readonly directory: Schema.Struct<{
|
|
20
6
|
readonly ticker: Schema.String;
|
|
@@ -108,13 +94,6 @@ export declare const CompanyRecord: Schema.Struct<{
|
|
|
108
94
|
}>;
|
|
109
95
|
}>>;
|
|
110
96
|
}>>;
|
|
111
|
-
readonly abnNameMatch: Schema.optionalKey<Schema.Struct<{
|
|
112
|
-
readonly abn: Schema.String;
|
|
113
|
-
readonly acn: Schema.NullOr<Schema.String>;
|
|
114
|
-
readonly abnEntityName: Schema.NonEmptyString;
|
|
115
|
-
readonly rule: Schema.Literals<readonly ["full-name", "suffix-removed", "the-moved", "comma-ignored"]>;
|
|
116
|
-
readonly abnSnapshotDate: Schema.String;
|
|
117
|
-
}>>;
|
|
118
97
|
readonly missingAt: Schema.optionalKey<Schema.Struct<{
|
|
119
98
|
readonly startedAt: Schema.String;
|
|
120
99
|
readonly observedAt: Schema.String;
|
|
@@ -218,13 +197,6 @@ declare const Dataset: Schema.Struct<{
|
|
|
218
197
|
}>;
|
|
219
198
|
}>>;
|
|
220
199
|
}>>;
|
|
221
|
-
readonly abnNameMatch: Schema.optionalKey<Schema.Struct<{
|
|
222
|
-
readonly abn: Schema.String;
|
|
223
|
-
readonly acn: Schema.NullOr<Schema.String>;
|
|
224
|
-
readonly abnEntityName: Schema.NonEmptyString;
|
|
225
|
-
readonly rule: Schema.Literals<readonly ["full-name", "suffix-removed", "the-moved", "comma-ignored"]>;
|
|
226
|
-
readonly abnSnapshotDate: Schema.String;
|
|
227
|
-
}>>;
|
|
228
200
|
readonly missingAt: Schema.optionalKey<Schema.Struct<{
|
|
229
201
|
readonly startedAt: Schema.String;
|
|
230
202
|
readonly observedAt: Schema.String;
|
|
@@ -303,11 +275,6 @@ export interface AsxSnapshot {
|
|
|
303
275
|
directory: DirectoryEntry;
|
|
304
276
|
enrichment: ConfirmedEnrichment;
|
|
305
277
|
}[];
|
|
306
|
-
/** Listed companies whose name matches this ABN's entity name under the #385 rules. */
|
|
307
|
-
readonly findNameMatchesByAbn: (abn: string) => readonly {
|
|
308
|
-
directory: DirectoryEntry;
|
|
309
|
-
nameMatch: AbnNameMatch;
|
|
310
|
-
}[];
|
|
311
278
|
readonly metadata: () => {
|
|
312
279
|
directoryObservedAt: string;
|
|
313
280
|
records: number;
|
package/dist/snapshot.js
CHANGED
|
@@ -4,24 +4,11 @@ import { DirectoryEntry, failure, isTimestamp, Observation, validObservation } f
|
|
|
4
4
|
import { normalizeTicker } from "./parse.js";
|
|
5
5
|
import { ConfirmedEnrichment, validAbn, validEnrichment } from "./enrichment.js";
|
|
6
6
|
import { CompanySources, decodeCompanySources } from "./source-record.js";
|
|
7
|
-
/**
|
|
8
|
-
* An ABN matched to a listed company by the owner-accepted name rules (#385): the
|
|
9
|
-
* full legal name, one trailing company suffix removed, a trailing "(THE)" moved to
|
|
10
|
-
* the front, or a comma ignored. A name match, never proof of issuer identity.
|
|
11
|
-
*/
|
|
12
|
-
export const AbnNameMatch = Schema.Struct({
|
|
13
|
-
abn: Schema.String.check(Schema.makeFilter(validAbn)),
|
|
14
|
-
acn: Schema.NullOr(Schema.String.check(Schema.isPattern(/^\d{9}$/))),
|
|
15
|
-
abnEntityName: Schema.NonEmptyString,
|
|
16
|
-
rule: Schema.Literals(["full-name", "suffix-removed", "the-moved", "comma-ignored"]),
|
|
17
|
-
abnSnapshotDate: Schema.String.check(Schema.isPattern(/^\d{4}-\d{2}-\d{2}$/)),
|
|
18
|
-
});
|
|
19
7
|
export const CompanyRecord = Schema.Struct({
|
|
20
8
|
directory: DirectoryEntry,
|
|
21
9
|
observation: Schema.NullOr(Observation),
|
|
22
10
|
sources: Schema.optionalKey(CompanySources),
|
|
23
11
|
enrichment: Schema.optionalKey(ConfirmedEnrichment),
|
|
24
|
-
abnNameMatch: Schema.optionalKey(AbnNameMatch),
|
|
25
12
|
missingAt: Schema.optionalKey(Schema.Struct({ startedAt: Schema.String, observedAt: Schema.String })),
|
|
26
13
|
});
|
|
27
14
|
const Dataset = Schema.Struct({
|
|
@@ -131,13 +118,6 @@ export async function openSnapshot(text, expectedGeneration) {
|
|
|
131
118
|
? [{ directory: { ...record.directory }, enrichment: structuredClone(record.enrichment) }]
|
|
132
119
|
: []);
|
|
133
120
|
},
|
|
134
|
-
findNameMatchesByAbn: (abn) => {
|
|
135
|
-
if (typeof abn !== "string" || !validAbn(abn))
|
|
136
|
-
throw failure("INVALID_INPUT");
|
|
137
|
-
return dataset.records.flatMap((record) => record.abnNameMatch?.abn === abn
|
|
138
|
-
? [{ directory: { ...record.directory }, nameMatch: { ...record.abnNameMatch } }]
|
|
139
|
-
: []);
|
|
140
|
-
},
|
|
141
121
|
metadata: () => ({
|
|
142
122
|
directoryObservedAt: dataset.directoryObservedAt,
|
|
143
123
|
records: records.size,
|
package/package.json
CHANGED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { HttpApi, HttpApiEndpoint, HttpApiGroup, OpenApi } from "effect/unstable/httpapi";
|
|
3
|
-
export declare const ASX_API_MAJOR = 1;
|
|
4
|
-
export declare const ASX_API_PREFIX = "/asx/v1";
|
|
5
|
-
export declare const AsxApi: HttpApi.HttpApi<"asx", HttpApiGroup.HttpApiGroup<"asx", HttpApiEndpoint.HttpApiEndpoint<"lookup", "POST", "/asx/v1/lookup", never, never, Schema.toCodecJson<Schema.Struct<{
|
|
6
|
-
readonly ticker: Schema.String;
|
|
7
|
-
readonly mode: Schema.optionalKey<Schema.Literals<readonly ["live", "snapshot", "live-first"]>>;
|
|
8
|
-
}>>, never, Schema.toCodecJson<Schema.Union<readonly [Schema.Struct<{
|
|
9
|
-
readonly status: Schema.Literal<"found">;
|
|
10
|
-
readonly source: Schema.Literal<"live">;
|
|
11
|
-
readonly observation: Schema.Struct<{
|
|
12
|
-
readonly company: Schema.Struct<{
|
|
13
|
-
readonly raw: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
|
|
14
|
-
readonly ticker: Schema.String;
|
|
15
|
-
readonly sourceId: Schema.String;
|
|
16
|
-
readonly companyName: Schema.String;
|
|
17
|
-
readonly address: Schema.NullOr<Schema.String>;
|
|
18
|
-
readonly tel: Schema.NullOr<Schema.String>;
|
|
19
|
-
readonly fax: Schema.NullOr<Schema.String>;
|
|
20
|
-
readonly website: Schema.NullOr<Schema.String>;
|
|
21
|
-
readonly description: Schema.NullOr<Schema.String>;
|
|
22
|
-
readonly issueType: Schema.NullOr<Schema.String>;
|
|
23
|
-
readonly foreignExempt: Schema.NullOr<Schema.Boolean>;
|
|
24
|
-
readonly people: Schema.$Array<Schema.Struct<{
|
|
25
|
-
readonly sourceName: Schema.String;
|
|
26
|
-
readonly displayName: Schema.String;
|
|
27
|
-
readonly title: Schema.String;
|
|
28
|
-
readonly sourceCategory: Schema.Literals<readonly ["directors", "secretaries"]>;
|
|
29
|
-
}>>;
|
|
30
|
-
}>;
|
|
31
|
-
readonly startedAt: Schema.String;
|
|
32
|
-
readonly observedAt: Schema.String;
|
|
33
|
-
readonly lastChangedAt: Schema.String;
|
|
34
|
-
}>;
|
|
35
|
-
}>, Schema.Struct<{
|
|
36
|
-
readonly status: Schema.Literal<"missing">;
|
|
37
|
-
readonly source: Schema.Literal<"live">;
|
|
38
|
-
readonly startedAt: Schema.String;
|
|
39
|
-
readonly observedAt: Schema.String;
|
|
40
|
-
}>, Schema.Struct<{
|
|
41
|
-
readonly status: Schema.Literal<"found">;
|
|
42
|
-
readonly source: Schema.Literal<"snapshot">;
|
|
43
|
-
readonly observation: Schema.Struct<{
|
|
44
|
-
readonly company: Schema.Struct<{
|
|
45
|
-
readonly raw: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Codec<Schema.Json, Schema.Json, never, never>>>;
|
|
46
|
-
readonly ticker: Schema.String;
|
|
47
|
-
readonly sourceId: Schema.String;
|
|
48
|
-
readonly companyName: Schema.String;
|
|
49
|
-
readonly address: Schema.NullOr<Schema.String>;
|
|
50
|
-
readonly tel: Schema.NullOr<Schema.String>;
|
|
51
|
-
readonly fax: Schema.NullOr<Schema.String>;
|
|
52
|
-
readonly website: Schema.NullOr<Schema.String>;
|
|
53
|
-
readonly description: Schema.NullOr<Schema.String>;
|
|
54
|
-
readonly issueType: Schema.NullOr<Schema.String>;
|
|
55
|
-
readonly foreignExempt: Schema.NullOr<Schema.Boolean>;
|
|
56
|
-
readonly people: Schema.$Array<Schema.Struct<{
|
|
57
|
-
readonly sourceName: Schema.String;
|
|
58
|
-
readonly displayName: Schema.String;
|
|
59
|
-
readonly title: Schema.String;
|
|
60
|
-
readonly sourceCategory: Schema.Literals<readonly ["directors", "secretaries"]>;
|
|
61
|
-
}>>;
|
|
62
|
-
}>;
|
|
63
|
-
readonly startedAt: Schema.String;
|
|
64
|
-
readonly observedAt: Schema.String;
|
|
65
|
-
readonly lastChangedAt: Schema.String;
|
|
66
|
-
}>;
|
|
67
|
-
readonly generation: Schema.String;
|
|
68
|
-
readonly enrichment: Schema.optionalKey<Schema.Struct<{
|
|
69
|
-
readonly ticker: Schema.String;
|
|
70
|
-
readonly sourceId: Schema.NonEmptyString;
|
|
71
|
-
readonly abn: Schema.String;
|
|
72
|
-
readonly acn: Schema.NullOr<Schema.String>;
|
|
73
|
-
readonly legalName: Schema.NonEmptyString;
|
|
74
|
-
readonly identityEvidence: Schema.Struct<{
|
|
75
|
-
readonly url: Schema.String;
|
|
76
|
-
readonly sha256: Schema.String;
|
|
77
|
-
readonly observedAt: Schema.String;
|
|
78
|
-
readonly kind: Schema.Literals<readonly ["issuer-filing", "issuer-website"]>;
|
|
79
|
-
}>;
|
|
80
|
-
readonly abrEvidence: Schema.Struct<{
|
|
81
|
-
readonly source: Schema.Literals<readonly ["abr-live", "abr-snapshot"]>;
|
|
82
|
-
readonly observedAt: Schema.String;
|
|
83
|
-
}>;
|
|
84
|
-
readonly website: Schema.NullOr<Schema.Struct<{
|
|
85
|
-
readonly url: Schema.String;
|
|
86
|
-
readonly evidence: Schema.Struct<{
|
|
87
|
-
readonly url: Schema.String;
|
|
88
|
-
readonly sha256: Schema.String;
|
|
89
|
-
readonly observedAt: Schema.String;
|
|
90
|
-
readonly kind: Schema.Literals<readonly ["issuer-filing", "issuer-website"]>;
|
|
91
|
-
}>;
|
|
92
|
-
}>>;
|
|
93
|
-
}>>;
|
|
94
|
-
readonly requiresRecheck: Schema.Literal<true>;
|
|
95
|
-
readonly fallbackReason: Schema.optionalKey<Schema.Literals<readonly ["INVALID_INPUT", "PARSE_FAILED", "SOURCE_MISMATCH", "TRANSPORT_FAILED", "TIMEOUT", "RATE_LIMITED", "UPSTREAM_UNAVAILABLE", "CONFIGURATION_ERROR", "SNAPSHOT_UNAVAILABLE", "CANCELLED", "INTERNAL_ERROR", "ACCESS_DENIED", "UPSTREAM_REJECTED", "PERSISTENCE_FAILED", "PUBLICATION_CONFLICT", "COVERAGE_REJECTED"]>>;
|
|
96
|
-
}>, Schema.Struct<{
|
|
97
|
-
readonly status: Schema.Literal<"missing">;
|
|
98
|
-
readonly source: Schema.Literal<"snapshot">;
|
|
99
|
-
readonly generation: Schema.String;
|
|
100
|
-
}>]>>, Schema.toCodecJson<Schema.Struct<{
|
|
101
|
-
readonly code: Schema.Literals<readonly [string, ...string[]]>;
|
|
102
|
-
readonly message: Schema.String;
|
|
103
|
-
readonly retryable: Schema.Boolean;
|
|
104
|
-
readonly requestId: Schema.String;
|
|
105
|
-
}>>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"suggest", "POST", "/asx/v1/suggest", never, never, Schema.toCodecJson<Schema.Struct<{
|
|
106
|
-
readonly mode: Schema.optionalKey<Schema.Literals<readonly ["live", "snapshot", "live-first"]>>;
|
|
107
|
-
readonly query: Schema.String;
|
|
108
|
-
readonly limit: Schema.optionalKey<Schema.Number>;
|
|
109
|
-
}>>, never, Schema.toCodecJson<Schema.Union<readonly [Schema.Struct<{
|
|
110
|
-
readonly source: Schema.Literal<"live">;
|
|
111
|
-
readonly directoryObservedAt: Schema.String;
|
|
112
|
-
readonly suggestions: Schema.$Array<Schema.Struct<{
|
|
113
|
-
readonly ticker: Schema.String;
|
|
114
|
-
readonly companyName: Schema.String;
|
|
115
|
-
readonly industryGroup: Schema.NullOr<Schema.String>;
|
|
116
|
-
readonly listingDate: Schema.NullOr<Schema.String>;
|
|
117
|
-
readonly marketCapitalisation: Schema.NullOr<Schema.Number>;
|
|
118
|
-
}>>;
|
|
119
|
-
}>, Schema.Struct<{
|
|
120
|
-
readonly source: Schema.Literal<"snapshot">;
|
|
121
|
-
readonly generation: Schema.String;
|
|
122
|
-
readonly directoryObservedAt: Schema.String;
|
|
123
|
-
readonly requiresRecheck: Schema.Literal<true>;
|
|
124
|
-
readonly suggestions: Schema.$Array<Schema.Struct<{
|
|
125
|
-
readonly ticker: Schema.String;
|
|
126
|
-
readonly companyName: Schema.String;
|
|
127
|
-
readonly industryGroup: Schema.NullOr<Schema.String>;
|
|
128
|
-
readonly listingDate: Schema.NullOr<Schema.String>;
|
|
129
|
-
readonly marketCapitalisation: Schema.NullOr<Schema.Number>;
|
|
130
|
-
}>>;
|
|
131
|
-
readonly fallbackReason: Schema.optionalKey<Schema.Literals<readonly ["INVALID_INPUT", "PARSE_FAILED", "SOURCE_MISMATCH", "TRANSPORT_FAILED", "TIMEOUT", "RATE_LIMITED", "UPSTREAM_UNAVAILABLE", "CONFIGURATION_ERROR", "SNAPSHOT_UNAVAILABLE", "CANCELLED", "INTERNAL_ERROR", "ACCESS_DENIED", "UPSTREAM_REJECTED", "PERSISTENCE_FAILED", "PUBLICATION_CONFLICT", "COVERAGE_REJECTED"]>>;
|
|
132
|
-
}>]>>, Schema.toCodecJson<Schema.Struct<{
|
|
133
|
-
readonly code: Schema.Literals<readonly [string, ...string[]]>;
|
|
134
|
-
readonly message: Schema.String;
|
|
135
|
-
readonly retryable: Schema.Boolean;
|
|
136
|
-
readonly requestId: Schema.String;
|
|
137
|
-
}>>, never, never>, false>>;
|
|
138
|
-
/** The OpenAPI document for the ASX contract. */
|
|
139
|
-
export declare const asxOpenApiDocument: () => OpenApi.OpenAPISpec;
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi, } from "effect/unstable/httpapi";
|
|
3
|
-
import { LookupResponse, SuggestionsResponse } from "../client-replies.js";
|
|
4
|
-
import { AsxLookupRequestSchema, AsxSuggestRequestSchema } from "../requests.js";
|
|
5
|
-
// The ASX HTTP contract (ADR-0052, platform #515 wave 3, T2), built from the package's own
|
|
6
|
-
// request and reply schemas.
|
|
7
|
-
const errorBody = (name, codes, status, description) => Schema.Struct({
|
|
8
|
-
code: Schema.Literals(codes),
|
|
9
|
-
message: Schema.String,
|
|
10
|
-
retryable: Schema.Boolean,
|
|
11
|
-
requestId: Schema.String.annotate({ description: "The request's id, for support." }),
|
|
12
|
-
})
|
|
13
|
-
.annotate({ identifier: `AsxError_${name}`, description })
|
|
14
|
-
.pipe(HttpApiSchema.status(status));
|
|
15
|
-
const ERRORS = [
|
|
16
|
-
errorBody("INVALID_INPUT", ["INVALID_INPUT"], 400, "The body is not valid for this operation."),
|
|
17
|
-
errorBody("UNAUTHORIZED", ["UNAUTHORIZED"], 401, "The request carries no identified caller (ADR-0018)."),
|
|
18
|
-
errorBody("VERSION_RETIRED", ["VERSION_RETIRED"], 410, "This major version is retired (ADR-0050)."),
|
|
19
|
-
errorBody("FAIR_USE_CEILING", ["FAIR_USE_CEILING"], 429, "The fair-use ceiling is reached; retry after Retry-After."),
|
|
20
|
-
errorBody("UPSTREAM", ["UPSTREAM_REJECTED", "PARSE_FAILED", "SOURCE_MISMATCH"], 502, "ASX answered, but its answer could not be used."),
|
|
21
|
-
errorBody("UNAVAILABLE", [
|
|
22
|
-
"TRANSPORT_FAILED",
|
|
23
|
-
"RATE_LIMITED",
|
|
24
|
-
"UPSTREAM_UNAVAILABLE",
|
|
25
|
-
"ACCESS_DENIED",
|
|
26
|
-
"CONFIGURATION_ERROR",
|
|
27
|
-
"SNAPSHOT_UNAVAILABLE",
|
|
28
|
-
"PERSISTENCE_FAILED",
|
|
29
|
-
], 503, "The source is unavailable."),
|
|
30
|
-
errorBody("TIMEOUT", ["TIMEOUT"], 504, "The operation did not finish within its deadline."),
|
|
31
|
-
];
|
|
32
|
-
export const ASX_API_MAJOR = 1;
|
|
33
|
-
export const ASX_API_PREFIX = `/asx/v${ASX_API_MAJOR}`;
|
|
34
|
-
const naming = (method) => ({
|
|
35
|
-
"x-fern-sdk-group-name": "asx",
|
|
36
|
-
"x-fern-sdk-method-name": method,
|
|
37
|
-
"x-fern-availability": "beta",
|
|
38
|
-
"x-comms-id-safe-to-retry": true,
|
|
39
|
-
"x-comms-id-auth": { audience: "asx-lookup", scope: "asx" },
|
|
40
|
-
});
|
|
41
|
-
const LOOKUP_EXAMPLE = Schema.decodeUnknownSync(LookupResponse)({
|
|
42
|
-
status: "missing",
|
|
43
|
-
source: "live",
|
|
44
|
-
startedAt: "2026-10-03T00:00:00.000Z",
|
|
45
|
-
observedAt: "2026-10-03T00:00:01.000Z",
|
|
46
|
-
});
|
|
47
|
-
const SUGGEST_EXAMPLE = Schema.decodeUnknownSync(SuggestionsResponse)({
|
|
48
|
-
source: "live",
|
|
49
|
-
directoryObservedAt: "2026-10-03T00:00:00.000Z",
|
|
50
|
-
suggestions: [],
|
|
51
|
-
});
|
|
52
|
-
// Request examples, decoded once through their schemas so an invalid one fails at load.
|
|
53
|
-
const exampleOf = (schema, value) => {
|
|
54
|
-
Schema.decodeUnknownSync(schema)(value);
|
|
55
|
-
return value;
|
|
56
|
-
};
|
|
57
|
-
const REQUEST_EXAMPLES = {
|
|
58
|
-
lookup: exampleOf(AsxLookupRequestSchema, { ticker: "BHP" }),
|
|
59
|
-
suggest: exampleOf(AsxSuggestRequestSchema, { query: "BH", limit: 5 }),
|
|
60
|
-
};
|
|
61
|
-
export const AsxApi = HttpApi.make("asx")
|
|
62
|
-
.add(HttpApiGroup.make("asx")
|
|
63
|
-
.add(HttpApiEndpoint.post("lookup", `${ASX_API_PREFIX}/lookup`, {
|
|
64
|
-
payload: AsxLookupRequestSchema,
|
|
65
|
-
success: LookupResponse.annotate({ examples: [LOOKUP_EXAMPLE] }),
|
|
66
|
-
error: ERRORS,
|
|
67
|
-
})
|
|
68
|
-
.annotate(OpenApi.Description, "Look up an ASX-listed company by ticker, live or from the stored directory.")
|
|
69
|
-
.annotate(OpenApi.Override, naming("lookup")))
|
|
70
|
-
.add(HttpApiEndpoint.post("suggest", `${ASX_API_PREFIX}/suggest`, {
|
|
71
|
-
payload: AsxSuggestRequestSchema,
|
|
72
|
-
success: SuggestionsResponse.annotate({ examples: [SUGGEST_EXAMPLE] }),
|
|
73
|
-
error: ERRORS,
|
|
74
|
-
})
|
|
75
|
-
.annotate(OpenApi.Description, "Suggest ASX-listed companies for a ticker or name prefix, at most ten.")
|
|
76
|
-
.annotate(OpenApi.Override, naming("suggest"))))
|
|
77
|
-
.annotate(OpenApi.Title, "Comms.ID ASX")
|
|
78
|
-
.annotate(OpenApi.Version, `${ASX_API_MAJOR}.0.0`)
|
|
79
|
-
// The Sydney front door (TLS terminates in Australia). Its pool routes /asx/* here and
|
|
80
|
-
// strips the prefix; the worker accepts /asx/v1/* and /v1/* alike.
|
|
81
|
-
.annotate(OpenApi.Servers, [
|
|
82
|
-
{
|
|
83
|
-
url: "https://o5tejx16gc.execute-api.ap-southeast-2.amazonaws.com",
|
|
84
|
-
description: "Sydney front door (TLS terminates in Australia)",
|
|
85
|
-
},
|
|
86
|
-
])
|
|
87
|
-
// The request examples go in the named map of each request media type (the convention of
|
|
88
|
-
// every Comms.ID contract).
|
|
89
|
-
.annotate(OpenApi.Transform, (spec) => {
|
|
90
|
-
const { paths } = spec;
|
|
91
|
-
for (const [operation, value] of Object.entries(REQUEST_EXAMPLES)) {
|
|
92
|
-
const media = paths[`${ASX_API_PREFIX}/${operation}`]?.post?.requestBody?.content["application/json"];
|
|
93
|
-
if (media !== undefined) {
|
|
94
|
-
media.examples = { default: { value } };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return spec;
|
|
98
|
-
});
|
|
99
|
-
/** The OpenAPI document for the ASX contract. */
|
|
100
|
-
export const asxOpenApiDocument = () => OpenApi.fromApi(AsxApi);
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { CompanySources, SourceName } from "./source-record.js";
|
|
2
|
-
export interface ProfileContact {
|
|
3
|
-
readonly address: string | null;
|
|
4
|
-
readonly tel: string | null;
|
|
5
|
-
readonly website: string | null;
|
|
6
|
-
/** Which collected source supplied each value; absent when the value is null. */
|
|
7
|
-
readonly sources: Partial<Record<"address" | "tel" | "website", SourceName>>;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Contact details from collected public profiles, used only where the ASX profile is blank.
|
|
11
|
-
* Values are never inferred: each comes verbatim from one named source.
|
|
12
|
-
*/
|
|
13
|
-
export declare function profileContact(sources: CompanySources): ProfileContact;
|
package/dist/profile-contact.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const PHONE = /^T\s+(\+?[\d\s()-]{6,})$/;
|
|
2
|
-
const WEB = /^(https?:\/\/|www\.)/i;
|
|
3
|
-
const AU_TAIL = /^(.+),\s*([^,]+?),?\s+(ACT|NSW|NT|QLD|SA|TAS|VIC|WA)\s+(\d{4})$/i;
|
|
4
|
-
/** Morningstar writes "street, suburb, STATE 3000"; ASX's own field is "street, suburb, STATE, Australia, 3000". */
|
|
5
|
-
const asxAddressFormat = (line) => {
|
|
6
|
-
const match = AU_TAIL.exec(line);
|
|
7
|
-
return match
|
|
8
|
-
? `${match[1]}, ${match[2]}, ${match[3].toUpperCase()}, Australia, ${match[4]}`
|
|
9
|
-
: line;
|
|
10
|
-
};
|
|
11
|
-
const profileText = (payload, selector) => {
|
|
12
|
-
const response = payload?.response;
|
|
13
|
-
const match = response?.result?.find((entry) => entry.selector === selector);
|
|
14
|
-
return match?.results?.map((result) => result.text ?? "").join("\n") ?? "";
|
|
15
|
-
};
|
|
16
|
-
/** Morningstar's contact block: "Contact", the address line, "T <phone>", then the website. */
|
|
17
|
-
const morningstarContact = (payload) => {
|
|
18
|
-
const lines = profileText(payload, ".sal-component-body:has(.sal-dp-pair-contact)")
|
|
19
|
-
.split("\n")
|
|
20
|
-
.map((line) => line.trim());
|
|
21
|
-
const start = lines.indexOf("Contact");
|
|
22
|
-
if (start < 0) {
|
|
23
|
-
return { address: null, tel: null };
|
|
24
|
-
}
|
|
25
|
-
let address = null;
|
|
26
|
-
let tel = null;
|
|
27
|
-
for (const line of lines.slice(start + 1, start + 5)) {
|
|
28
|
-
const phone = PHONE.exec(line);
|
|
29
|
-
if (phone?.[1]) {
|
|
30
|
-
tel = phone[1].trim();
|
|
31
|
-
}
|
|
32
|
-
else if (!(address || WEB.test(line)) && line.length > 5 && /\d/.test(line)) {
|
|
33
|
-
address = asxAddressFormat(line);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { address, tel };
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Contact details from collected public profiles, used only where the ASX profile is blank.
|
|
40
|
-
* Values are never inferred: each comes verbatim from one named source.
|
|
41
|
-
*/
|
|
42
|
-
export function profileContact(sources) {
|
|
43
|
-
const found = (name) => sources[name]?.status === "found" ? sources[name] : undefined;
|
|
44
|
-
const morningstar = found("morningstar");
|
|
45
|
-
const google = found("google-finance");
|
|
46
|
-
const contact = morningstar
|
|
47
|
-
? morningstarContact(morningstar.payload)
|
|
48
|
-
: { address: null, tel: null };
|
|
49
|
-
const website = morningstar?.websites[0] ?? google?.websites[0] ?? null;
|
|
50
|
-
return {
|
|
51
|
-
address: contact.address,
|
|
52
|
-
tel: contact.tel,
|
|
53
|
-
website,
|
|
54
|
-
sources: {
|
|
55
|
-
...(contact.address ? { address: "morningstar" } : {}),
|
|
56
|
-
...(contact.tel ? { tel: "morningstar" } : {}),
|
|
57
|
-
...(website
|
|
58
|
-
? {
|
|
59
|
-
website: morningstar?.websites[0]
|
|
60
|
-
? "morningstar"
|
|
61
|
-
: "google-finance",
|
|
62
|
-
}
|
|
63
|
-
: {}),
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
package/dist/requests.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
export declare const AsxLookupRequestSchema: Schema.Struct<{
|
|
3
|
-
readonly ticker: Schema.String;
|
|
4
|
-
readonly mode: Schema.optionalKey<Schema.Literals<readonly ["live", "snapshot", "live-first"]>>;
|
|
5
|
-
}>;
|
|
6
|
-
export declare const AsxSuggestRequestSchema: Schema.Struct<{
|
|
7
|
-
readonly mode: Schema.optionalKey<Schema.Literals<readonly ["live", "snapshot", "live-first"]>>;
|
|
8
|
-
readonly query: Schema.String;
|
|
9
|
-
readonly limit: Schema.optionalKey<Schema.Number>;
|
|
10
|
-
}>;
|
package/dist/requests.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
// Request bodies for the hosted ASX service (platform #515): the worker decodes with these,
|
|
3
|
-
// and the v1 contract is built from them.
|
|
4
|
-
const Mode = Schema.Literals(["live", "snapshot", "live-first"]).annotate({
|
|
5
|
-
description: "live: ASX now; snapshot: the stored directory; live-first: ASX, then the snapshot on failure.",
|
|
6
|
-
});
|
|
7
|
-
export const AsxLookupRequestSchema = Schema.Struct({
|
|
8
|
-
ticker: Schema.String.check(Schema.isMaxLength(128)).annotate({
|
|
9
|
-
description: "The ASX ticker, for example 'BHP'.",
|
|
10
|
-
}),
|
|
11
|
-
mode: Schema.optionalKey(Mode),
|
|
12
|
-
});
|
|
13
|
-
export const AsxSuggestRequestSchema = Schema.Struct({
|
|
14
|
-
mode: Schema.optionalKey(Mode),
|
|
15
|
-
query: Schema.String.check(Schema.isMaxLength(200)).annotate({
|
|
16
|
-
description: "A ticker or company name prefix.",
|
|
17
|
-
}),
|
|
18
|
-
limit: Schema.optionalKey(Schema.Number.check(Schema.isInt(), Schema.isBetween({ minimum: 1, maximum: 10 })).annotate({
|
|
19
|
-
description: "How many suggestions to return, 1 to 10.",
|
|
20
|
-
})),
|
|
21
|
-
});
|