@jd-data-limited/easy-fm 5.0.0-beta.1 → 5.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +93 -90
- package/dist/bin/cli.js +34 -5
- package/dist/bin/generateClient.d.ts +1 -0
- package/dist/bin/generateClient.js +48 -0
- package/dist/bin/generateTypes.js +20 -19
- package/dist/bin/init.d.ts +6 -0
- package/dist/bin/init.js +46 -0
- package/dist/bin/stressSearch.js +12 -12
- package/dist/connection/DataApiProvider.d.ts +15 -0
- package/dist/connection/DataApiProvider.js +246 -0
- package/dist/connection/FMHost.d.ts +1 -3
- package/dist/connection/FMHost.js +1 -3
- package/dist/connection/HostBase.d.ts +0 -2
- package/dist/connection/dataApiDatabaseSession.d.ts +11 -0
- package/dist/connection/dataApiDatabaseSession.js +139 -0
- package/dist/connection/database.d.ts +22 -56
- package/dist/connection/database.js +175 -111
- package/dist/connection/databaseBase.d.ts +9 -22
- package/dist/connection/databaseBase.js +0 -3
- package/dist/connection/databaseProvider.d.ts +196 -0
- package/dist/connection/databaseProvider.js +6 -0
- package/dist/connection/databaseSessionPool.d.ts +8 -7
- package/dist/connection/databaseSessionPool.js +110 -106
- package/dist/index.d.ts +11 -7
- package/dist/index.js +7 -5
- package/dist/layouts/layout.d.ts +0 -2
- package/dist/layouts/layout.js +6 -16
- package/dist/layouts/layoutBase.d.ts +0 -1
- package/dist/layouts/layoutInterface.d.ts +4 -1
- package/dist/models/apiResults.d.ts +7 -6
- package/dist/models/apiResults.js +1 -1
- package/dist/records/fields/baseField.d.ts +60 -0
- package/dist/records/fields/baseField.js +59 -0
- package/dist/records/fields/containerField.d.ts +18 -0
- package/dist/records/fields/containerField.js +44 -0
- package/dist/records/fields/field.d.ts +3 -0
- package/dist/records/fields/field.js +1 -0
- package/dist/records/fields/valueField.d.ts +41 -0
- package/dist/records/fields/valueField.js +108 -0
- package/dist/records/getOperations/recordGetOperation.js +24 -24
- package/dist/records/layoutRecord.d.ts +15 -8
- package/dist/records/layoutRecord.js +61 -98
- package/dist/records/portal.d.ts +1 -1
- package/dist/records/portalBase.d.ts +1 -2
- package/dist/records/portalRecord.d.ts +11 -5
- package/dist/records/portalRecord.js +24 -2
- package/dist/records/recordBase.d.ts +11 -7
- package/dist/records/recordBase.js +38 -38
- package/dist/schema.d.ts +54 -0
- package/dist/schema.js +87 -0
- package/dist/typegen/dataApiSchemaSource.d.ts +3 -0
- package/dist/typegen/dataApiSchemaSource.js +32 -0
- package/dist/typegen/dependencies.d.ts +11 -0
- package/dist/typegen/dependencies.js +67 -0
- package/dist/typegen/generate.d.ts +5 -0
- package/dist/typegen/generate.js +85 -0
- package/dist/typegen/index.d.ts +6 -0
- package/dist/typegen/index.js +6 -0
- package/dist/typegen/init.d.ts +13 -0
- package/dist/typegen/init.js +113 -0
- package/dist/typegen/types.d.ts +40 -0
- package/dist/typegen/types.js +1 -0
- package/dist/typegen/zod.d.ts +3 -0
- package/dist/typegen/zod.js +32 -0
- package/dist/types.d.ts +1 -1
- package/dist/utils/query.d.ts +21 -12
- package/dist/utils/query.js +58 -23
- package/dist/utils/temporal.d.ts +9 -0
- package/dist/utils/temporal.js +110 -0
- package/package.json +47 -24
- package/dist/layouts/recordFieldsMap.d.ts +0 -2
- package/dist/layouts/recordFieldsMap.js +0 -4
- package/dist/records/field.d.ts +0 -77
- package/dist/records/field.js +0 -192
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ApiFieldResultTypes, ApiFieldTypes } from '../models/apiResults.js';
|
|
2
|
+
export interface TypegenField {
|
|
3
|
+
name: string;
|
|
4
|
+
result: typeof ApiFieldResultTypes[keyof typeof ApiFieldResultTypes];
|
|
5
|
+
type: typeof ApiFieldTypes[keyof typeof ApiFieldTypes];
|
|
6
|
+
writable: boolean;
|
|
7
|
+
required: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface TypegenLayout {
|
|
10
|
+
name: string;
|
|
11
|
+
clientName?: string;
|
|
12
|
+
fields: TypegenField[];
|
|
13
|
+
portals: Record<string, TypegenField[]>;
|
|
14
|
+
}
|
|
15
|
+
export interface TypegenSchema {
|
|
16
|
+
version: 1;
|
|
17
|
+
layouts: TypegenLayout[];
|
|
18
|
+
}
|
|
19
|
+
export type TypegenTransport = 'data-api' | 'odata';
|
|
20
|
+
export interface SchemaSource {
|
|
21
|
+
introspect(): Promise<TypegenSchema>;
|
|
22
|
+
}
|
|
23
|
+
/** Code-generation plugin for a runtime validation library. */
|
|
24
|
+
export interface TypegenValidator {
|
|
25
|
+
readonly imports: readonly string[];
|
|
26
|
+
readonly runtimeValidator: string;
|
|
27
|
+
/** Render a validator for the complete canonical ProviderRecord. */
|
|
28
|
+
renderRecord(layout: TypegenLayout): string;
|
|
29
|
+
}
|
|
30
|
+
export interface TypegenConfig {
|
|
31
|
+
source: SchemaSource;
|
|
32
|
+
output: string;
|
|
33
|
+
/** Placeholder describing intended runtime transports. */
|
|
34
|
+
transports?: TypegenTransport[];
|
|
35
|
+
/** Restrict output to these FileMaker layout names. */
|
|
36
|
+
layouts?: string[];
|
|
37
|
+
/** Defaults to the built-in strict Zod validator. */
|
|
38
|
+
validator?: TypegenValidator;
|
|
39
|
+
}
|
|
40
|
+
export declare function defineTypegenConfig(config: TypegenConfig): TypegenConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function defineTypegenConfig(config) { return config; }
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Strict Zod generator. Extra, missing, and wrongly typed fields fail validation. */
|
|
2
|
+
export function zodTypegenValidator() {
|
|
3
|
+
return {
|
|
4
|
+
imports: ["import {z} from 'zod'"],
|
|
5
|
+
runtimeValidator: 'zodValidator',
|
|
6
|
+
renderRecord: renderProviderRecord
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function renderProviderRecord(layout) {
|
|
10
|
+
const fieldData = objectSchema(layout.fields);
|
|
11
|
+
const portals = Object.entries(layout.portals).map(([name, fields]) => `${quote(name)}: z.array(${portalRowSchema(fields)})`).join(', ');
|
|
12
|
+
const portalData = `z.object({${portals}}).partial().strict().optional()`;
|
|
13
|
+
return `z.object({recordId: z.string(), modId: z.string(), fieldData: ${fieldData}, portalData: ${portalData}}).strict()`;
|
|
14
|
+
}
|
|
15
|
+
function objectSchema(fields) {
|
|
16
|
+
return `z.object({${fields.map(field => `${quote(field.name)}: ${fieldSchema(field)}`).join(', ')}}).strict()`;
|
|
17
|
+
}
|
|
18
|
+
function portalRowSchema(fields) {
|
|
19
|
+
const entries = fields.map(field => `${quote(field.name)}: ${fieldSchema(field)}`);
|
|
20
|
+
entries.push('recordId: z.union([z.string(), z.number()])', 'modId: z.union([z.string(), z.number()])');
|
|
21
|
+
return `z.object({${entries.join(', ')}}).strict()`;
|
|
22
|
+
}
|
|
23
|
+
function fieldSchema(field) {
|
|
24
|
+
if (field.result === 'number')
|
|
25
|
+
return 'z.number().nullable()';
|
|
26
|
+
if (field.result === 'date' || field.result === 'time' || field.result === 'timeStamp')
|
|
27
|
+
return 'z.string().nullable()';
|
|
28
|
+
if (field.result === 'container')
|
|
29
|
+
return 'z.string().nullable()';
|
|
30
|
+
return 'z.string()';
|
|
31
|
+
}
|
|
32
|
+
function quote(value) { return JSON.stringify(value); }
|
package/dist/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type * as http from 'http';
|
|
|
2
2
|
import { type FMError } from './FMError.js';
|
|
3
3
|
import { type LayoutInterface } from './layouts/layoutInterface.js';
|
|
4
4
|
import z from "zod";
|
|
5
|
-
export type PickPortals<LAYOUT extends LayoutInterface, PORTALS extends
|
|
5
|
+
export type PickPortals<LAYOUT extends LayoutInterface, PORTALS extends keyof LAYOUT['portals'] = never> = Omit<LAYOUT, 'portals'> & {
|
|
6
6
|
portals: Pick<LAYOUT['portals'], PORTALS>;
|
|
7
7
|
};
|
|
8
8
|
/** Shared database connection options. */
|
package/dist/utils/query.d.ts
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Temporal } from 'temporal-polyfill';
|
|
2
2
|
export declare const FindRequestSymbol: unique symbol;
|
|
3
|
-
|
|
4
|
-
export interface TimestampType {
|
|
5
|
-
type: 'date' | 'time' | 'timestamp';
|
|
6
|
-
moment: Moment;
|
|
7
|
-
}
|
|
3
|
+
export type TimestampType = Temporal.PlainDate | Temporal.PlainTime | Temporal.PlainDateTime;
|
|
8
4
|
type QueryParameter = string | number | TimestampType;
|
|
9
5
|
/** Represents a FileMaker find value produced by `query(...)`. */
|
|
10
6
|
export interface Query {
|
|
11
7
|
[FindRequestSymbol]: Array<string | TimestampType>;
|
|
12
8
|
}
|
|
9
|
+
type LegacyDateLike = {
|
|
10
|
+
toDate: () => Date;
|
|
11
|
+
};
|
|
12
|
+
type DateInput = Date | LegacyDateLike;
|
|
13
|
+
/**
|
|
14
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal date.
|
|
15
|
+
* @deprecated Construct a `Temporal.PlainDate` directly instead.
|
|
16
|
+
*/
|
|
17
|
+
export declare function asDate(value: DateInput | Temporal.PlainDate | Temporal.PlainDateTime): Temporal.PlainDate;
|
|
18
|
+
/**
|
|
19
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal time.
|
|
20
|
+
* @deprecated Construct a `Temporal.PlainTime` directly instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare function asTime(value: DateInput | Temporal.PlainTime | Temporal.PlainDateTime): Temporal.PlainTime;
|
|
23
|
+
/**
|
|
24
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal timestamp.
|
|
25
|
+
* @deprecated Construct a `Temporal.PlainDateTime` directly instead.
|
|
26
|
+
*/
|
|
27
|
+
export declare function asTimestamp(value: DateInput | Temporal.PlainDateTime): Temporal.PlainDateTime;
|
|
13
28
|
/** Escapes special characters in a raw FileMaker find string. */
|
|
14
29
|
export declare function queryEscape(str: string): string;
|
|
15
30
|
/** Builds a FileMaker find value using template-string syntax. */
|
|
16
31
|
export declare function query(strings: TemplateStringsArray, ...args: QueryParameter[]): Query;
|
|
17
|
-
/** Marks a value as a FileMaker date for use inside `query(...)`. */
|
|
18
|
-
export declare function asDate(date: Date | Moment): TimestampType;
|
|
19
|
-
/** Marks a value as a FileMaker time for use inside `query(...)`. */
|
|
20
|
-
export declare function asTime(date: Date | Moment): TimestampType;
|
|
21
|
-
/** Marks a value as a FileMaker timestamp for use inside `query(...)`. */
|
|
22
|
-
export declare function asTimestamp(date: Date | Moment): TimestampType;
|
|
23
32
|
export {};
|
package/dist/utils/query.js
CHANGED
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Copyright (c) 2024. See LICENSE file for more information
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { Temporal } from 'temporal-polyfill';
|
|
5
5
|
export const FindRequestSymbol = Symbol('easyfm-findrequest');
|
|
6
6
|
const SPECIAL_CHARACTERS = ['\\', '=', '<', '≤', '≥', '>', '…', '...', '//', '@', '#', '*', '"', '~'];
|
|
7
|
+
function toDate(value) {
|
|
8
|
+
const date = value instanceof Date ? value : value.toDate();
|
|
9
|
+
if (Number.isNaN(date.getTime()))
|
|
10
|
+
throw new RangeError('Invalid date value');
|
|
11
|
+
return date;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal date.
|
|
15
|
+
* @deprecated Construct a `Temporal.PlainDate` directly instead.
|
|
16
|
+
*/
|
|
17
|
+
export function asDate(value) {
|
|
18
|
+
if (value instanceof Temporal.PlainDate)
|
|
19
|
+
return value;
|
|
20
|
+
if (value instanceof Temporal.PlainDateTime)
|
|
21
|
+
return value.toPlainDate();
|
|
22
|
+
const date = toDate(value);
|
|
23
|
+
return Temporal.PlainDate.from({
|
|
24
|
+
year: date.getFullYear(),
|
|
25
|
+
month: date.getMonth() + 1,
|
|
26
|
+
day: date.getDate()
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal time.
|
|
31
|
+
* @deprecated Construct a `Temporal.PlainTime` directly instead.
|
|
32
|
+
*/
|
|
33
|
+
export function asTime(value) {
|
|
34
|
+
if (value instanceof Temporal.PlainTime)
|
|
35
|
+
return value;
|
|
36
|
+
if (value instanceof Temporal.PlainDateTime)
|
|
37
|
+
return value.toPlainTime();
|
|
38
|
+
const date = toDate(value);
|
|
39
|
+
return Temporal.PlainTime.from({
|
|
40
|
+
hour: date.getHours(),
|
|
41
|
+
minute: date.getMinutes(),
|
|
42
|
+
second: date.getSeconds(),
|
|
43
|
+
millisecond: date.getMilliseconds()
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Converts a JavaScript Date or legacy Moment-like value to a Temporal timestamp.
|
|
48
|
+
* @deprecated Construct a `Temporal.PlainDateTime` directly instead.
|
|
49
|
+
*/
|
|
50
|
+
export function asTimestamp(value) {
|
|
51
|
+
if (value instanceof Temporal.PlainDateTime)
|
|
52
|
+
return value;
|
|
53
|
+
const date = toDate(value);
|
|
54
|
+
return Temporal.PlainDateTime.from({
|
|
55
|
+
year: date.getFullYear(),
|
|
56
|
+
month: date.getMonth() + 1,
|
|
57
|
+
day: date.getDate(),
|
|
58
|
+
hour: date.getHours(),
|
|
59
|
+
minute: date.getMinutes(),
|
|
60
|
+
second: date.getSeconds(),
|
|
61
|
+
millisecond: date.getMilliseconds()
|
|
62
|
+
});
|
|
63
|
+
}
|
|
7
64
|
/** Escapes special characters in a raw FileMaker find string. */
|
|
8
65
|
export function queryEscape(str) {
|
|
9
66
|
for (const char of SPECIAL_CHARACTERS) {
|
|
@@ -28,25 +85,3 @@ export function query(strings, ...args) {
|
|
|
28
85
|
}).flat(1);
|
|
29
86
|
return { [FindRequestSymbol]: query };
|
|
30
87
|
}
|
|
31
|
-
/** Marks a value as a FileMaker date for use inside `query(...)`. */
|
|
32
|
-
export function asDate(date) {
|
|
33
|
-
return {
|
|
34
|
-
type: 'date',
|
|
35
|
-
moment: moment(date)
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
/** Marks a value as a FileMaker time for use inside `query(...)`. */
|
|
39
|
-
export function asTime(date) {
|
|
40
|
-
return {
|
|
41
|
-
type: 'time',
|
|
42
|
-
moment: moment(date)
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/** Marks a value as a FileMaker timestamp for use inside `query(...)`. */
|
|
46
|
-
export function asTimestamp(date) {
|
|
47
|
-
return {
|
|
48
|
-
type: 'timestamp',
|
|
49
|
-
moment: moment(date)
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
query;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Temporal } from 'temporal-polyfill';
|
|
2
|
+
export type TemporalValue = Temporal.PlainDate | Temporal.PlainTime | Temporal.PlainDateTime;
|
|
3
|
+
export type TemporalValueType = 'date' | 'time' | 'timestamp';
|
|
4
|
+
/** Formats a Temporal value using the supplied token-based format. */
|
|
5
|
+
export declare function temporalToString(value: TemporalValue, format: string): string;
|
|
6
|
+
export declare function stringToTemporal(value: string, type: 'date', format: string): Temporal.PlainDate;
|
|
7
|
+
export declare function stringToTemporal(value: string, type: 'time', format: string): Temporal.PlainTime;
|
|
8
|
+
export declare function stringToTemporal(value: string, type: 'timestamp', format: string): Temporal.PlainDateTime;
|
|
9
|
+
export declare function stringToTemporal(value: string, type: TemporalValueType, format: string): TemporalValue;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2024. See LICENSE file for more information
|
|
3
|
+
*/
|
|
4
|
+
import { Temporal } from 'temporal-polyfill';
|
|
5
|
+
const FORMAT_TOKEN_PATTERN = /yyyy|MM|dd|HH|mm|ss/g;
|
|
6
|
+
const TOKEN_PATTERNS = {
|
|
7
|
+
yyyy: '\\d{4}',
|
|
8
|
+
MM: '\\d{2}',
|
|
9
|
+
dd: '\\d{2}',
|
|
10
|
+
HH: '\\d{2}',
|
|
11
|
+
mm: '\\d{2}',
|
|
12
|
+
ss: '\\d{2}'
|
|
13
|
+
};
|
|
14
|
+
function normalizeFormat(format) {
|
|
15
|
+
return format.replace(/YYYY/g, 'yyyy').replace(/DD/g, 'dd');
|
|
16
|
+
}
|
|
17
|
+
function pad(value, length = 2) {
|
|
18
|
+
return value.toString().padStart(length, '0');
|
|
19
|
+
}
|
|
20
|
+
function invalidTemporalString(value, format) {
|
|
21
|
+
throw new RangeError(`Invalid Temporal value ${JSON.stringify(value)}; expected ${format}`);
|
|
22
|
+
}
|
|
23
|
+
function escapeRegExp(value) {
|
|
24
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
25
|
+
}
|
|
26
|
+
function tokensIn(format) {
|
|
27
|
+
const tokens = format.match(FORMAT_TOKEN_PATTERN);
|
|
28
|
+
if (!tokens)
|
|
29
|
+
throw new RangeError(`Format ${JSON.stringify(format)} contains no supported tokens`);
|
|
30
|
+
if (new Set(tokens).size !== tokens.length) {
|
|
31
|
+
throw new RangeError(`Format ${JSON.stringify(format)} contains duplicate tokens`);
|
|
32
|
+
}
|
|
33
|
+
return tokens;
|
|
34
|
+
}
|
|
35
|
+
function parserFor(format) {
|
|
36
|
+
const tokens = tokensIn(format);
|
|
37
|
+
let source = '^';
|
|
38
|
+
let lastIndex = 0;
|
|
39
|
+
for (const match of format.matchAll(FORMAT_TOKEN_PATTERN)) {
|
|
40
|
+
source += escapeRegExp(format.slice(lastIndex, match.index));
|
|
41
|
+
source += `(${TOKEN_PATTERNS[match[0]]})`;
|
|
42
|
+
lastIndex = match.index + match[0].length;
|
|
43
|
+
}
|
|
44
|
+
source += `${escapeRegExp(format.slice(lastIndex))}$`;
|
|
45
|
+
return { pattern: new RegExp(source), tokens };
|
|
46
|
+
}
|
|
47
|
+
/** Formats a Temporal value using the supplied token-based format. */
|
|
48
|
+
export function temporalToString(value, format) {
|
|
49
|
+
format = normalizeFormat(format);
|
|
50
|
+
const values = {};
|
|
51
|
+
if (value instanceof Temporal.PlainDateTime) {
|
|
52
|
+
Object.assign(values, {
|
|
53
|
+
yyyy: pad(value.year, 4), MM: pad(value.month), dd: pad(value.day),
|
|
54
|
+
HH: pad(value.hour), mm: pad(value.minute), ss: pad(value.second)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (value instanceof Temporal.PlainDate) {
|
|
58
|
+
Object.assign(values, { yyyy: pad(value.year, 4), MM: pad(value.month), dd: pad(value.day) });
|
|
59
|
+
}
|
|
60
|
+
else if (value instanceof Temporal.PlainTime) {
|
|
61
|
+
Object.assign(values, { HH: pad(value.hour), mm: pad(value.minute), ss: pad(value.second) });
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw new TypeError('Expected a Temporal.PlainDate, Temporal.PlainTime, or Temporal.PlainDateTime');
|
|
65
|
+
}
|
|
66
|
+
tokensIn(format).forEach(token => {
|
|
67
|
+
if (values[token] === undefined)
|
|
68
|
+
throw new RangeError(`Token ${token} is not available for this Temporal type`);
|
|
69
|
+
});
|
|
70
|
+
return format.replace(FORMAT_TOKEN_PATTERN, token => values[token]);
|
|
71
|
+
}
|
|
72
|
+
/** Parses a Temporal value using the supplied token-based format. */
|
|
73
|
+
export function stringToTemporal(value, type, format) {
|
|
74
|
+
format = normalizeFormat(format);
|
|
75
|
+
const { pattern, tokens } = parserFor(format);
|
|
76
|
+
const match = pattern.exec(value);
|
|
77
|
+
if (!match)
|
|
78
|
+
return invalidTemporalString(value, format);
|
|
79
|
+
const parts = Object.fromEntries(tokens.map((token, index) => [token, Number(match[index + 1])]));
|
|
80
|
+
const required = type === 'date'
|
|
81
|
+
? ['yyyy', 'MM', 'dd']
|
|
82
|
+
: type === 'time'
|
|
83
|
+
? ['HH', 'mm', 'ss']
|
|
84
|
+
: ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss'];
|
|
85
|
+
if (tokens.length !== required.length || required.some(token => parts[token] === undefined)) {
|
|
86
|
+
throw new RangeError(`Format ${JSON.stringify(format)} does not match Temporal type ${type}`);
|
|
87
|
+
}
|
|
88
|
+
if (type === 'date') {
|
|
89
|
+
return Temporal.PlainDate.from({
|
|
90
|
+
year: parts.yyyy,
|
|
91
|
+
month: parts.MM,
|
|
92
|
+
day: parts.dd
|
|
93
|
+
}, { overflow: 'reject' });
|
|
94
|
+
}
|
|
95
|
+
else if (type === 'time') {
|
|
96
|
+
return Temporal.PlainTime.from({
|
|
97
|
+
hour: parts.HH,
|
|
98
|
+
minute: parts.mm,
|
|
99
|
+
second: parts.ss
|
|
100
|
+
}, { overflow: 'reject' });
|
|
101
|
+
}
|
|
102
|
+
return Temporal.PlainDateTime.from({
|
|
103
|
+
year: parts.yyyy,
|
|
104
|
+
month: parts.MM,
|
|
105
|
+
day: parts.dd,
|
|
106
|
+
hour: parts.HH,
|
|
107
|
+
minute: parts.mm,
|
|
108
|
+
second: parts.ss
|
|
109
|
+
}, { overflow: 'reject' });
|
|
110
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jd-data-limited/easy-fm",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.4",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"chalk": "^5.6.2",
|
|
6
|
+
"commander": "^15.0.0",
|
|
7
|
+
"inquirer": "^14.2.2",
|
|
8
|
+
"temporal-polyfill": "^1.0.5",
|
|
9
|
+
"zod": "^4.6.5"
|
|
8
10
|
},
|
|
9
11
|
"type": "module",
|
|
10
12
|
"main": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./typegen": {
|
|
21
|
+
"types": "./dist/typegen/index.d.ts",
|
|
22
|
+
"import": "./dist/typegen/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
11
25
|
"module": "es2022",
|
|
12
26
|
"homepage": "https://jd-data.com/project/easyfm/",
|
|
13
27
|
"url": "https://github.com/JD-Data-Limited/easy-fm",
|
|
@@ -40,41 +54,50 @@
|
|
|
40
54
|
"node": ">=22.22.3"
|
|
41
55
|
},
|
|
42
56
|
"devDependencies": {
|
|
43
|
-
"@babel/cli": "^
|
|
44
|
-
"@babel/core": "^
|
|
45
|
-
"@babel/preset-env": "^
|
|
46
|
-
"@babel/preset-typescript": "^
|
|
57
|
+
"@babel/cli": "^8.0.6",
|
|
58
|
+
"@babel/core": "^8.0.6",
|
|
59
|
+
"@babel/preset-env": "^8.0.6",
|
|
60
|
+
"@babel/preset-typescript": "^8.0.1",
|
|
61
|
+
"@cloudflare/vitest-plugin": "^1.2.2",
|
|
47
62
|
"@types/btoa": "^1.2.5",
|
|
48
63
|
"@types/events": "^3.0.3",
|
|
49
64
|
"@types/inquirer": "^9.0.10",
|
|
50
|
-
"@types/jest": "^
|
|
51
|
-
"@types/node": "^
|
|
52
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
53
|
-
"babel-jest": "^
|
|
54
|
-
"dotenv": "^
|
|
55
|
-
"eslint": "^
|
|
65
|
+
"@types/jest": "^30.0.0",
|
|
66
|
+
"@types/node": "^26.6.2",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.70.1",
|
|
68
|
+
"babel-jest": "^30.5.2",
|
|
69
|
+
"dotenv": "^18.0.2",
|
|
70
|
+
"eslint": "^10.11.0",
|
|
56
71
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
|
57
72
|
"eslint-plugin-import": "^2.32.0",
|
|
58
|
-
"eslint-plugin-n": "^
|
|
59
|
-
"eslint-plugin-promise": "^
|
|
60
|
-
"
|
|
61
|
-
"jest": "^29.
|
|
62
|
-
"ts-
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
73
|
+
"eslint-plugin-n": "^18.3.0",
|
|
74
|
+
"eslint-plugin-promise": "^7.3.0",
|
|
75
|
+
"jest": "^30.5.2",
|
|
76
|
+
"ts-jest": "^29.4.12",
|
|
77
|
+
"ts-morph": "^28.0.0",
|
|
78
|
+
"tsx": "^4.23.15",
|
|
79
|
+
"typedoc": "^0.28.20",
|
|
80
|
+
"typescript": "^6.0.3",
|
|
81
|
+
"vitest": "^4.1.11"
|
|
67
82
|
},
|
|
68
83
|
"bin": {
|
|
69
84
|
"easyfm": "dist/bin/cli.js"
|
|
70
85
|
},
|
|
86
|
+
"publishConfig": {
|
|
87
|
+
"access": "public"
|
|
88
|
+
},
|
|
71
89
|
"scripts": {
|
|
72
90
|
"build": "tsc",
|
|
73
91
|
"test": "npm run build && jest",
|
|
92
|
+
"test:types": "tsc --project tsconfig.test.json",
|
|
74
93
|
"test:coverage": "jest --coverage --testPathIgnorePatterns='/__tests__/database.test.ts$'",
|
|
75
94
|
"test:integration": "npm run build && jest __tests__/database.test.ts --runInBand",
|
|
76
95
|
"test:unit": "jest __tests__/query.test.ts __tests__/CookieJar.test.ts --runInBand",
|
|
96
|
+
"test:runtime": "pnpm build && pnpm test:runtime:bun && pnpm test:runtime:deno && pnpm test:runtime:workers",
|
|
97
|
+
"test:runtime:bun": "bun test ./runtime-tests/bun.test.ts",
|
|
98
|
+
"test:runtime:deno": "deno test --no-check --allow-read --node-modules-dir=manual ./runtime-tests/deno.test.ts",
|
|
99
|
+
"test:runtime:workers": "vitest run --config ./runtime-tests/workers/vitest.config.ts",
|
|
77
100
|
"stress:search": "tsx src/bin/stressSearch.ts",
|
|
78
|
-
"docs:build": "typedoc --options
|
|
101
|
+
"docs:build": "typedoc --options ../../docs/theme.json"
|
|
79
102
|
}
|
|
80
103
|
}
|
package/dist/records/field.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { RecordTypes } from '../types.js';
|
|
2
|
-
import { type ApiFieldMetadata } from '../models/apiResults.js';
|
|
3
|
-
import { type LayoutBase } from '../layouts/layoutBase.js';
|
|
4
|
-
import { type Moment } from 'moment';
|
|
5
|
-
import { Readable } from 'node:stream';
|
|
6
|
-
import { type z } from 'zod';
|
|
7
|
-
export type FieldValue = string | number | Moment | Container;
|
|
8
|
-
export type Container = null;
|
|
9
|
-
export interface Parentable {
|
|
10
|
-
layout: LayoutBase;
|
|
11
|
-
type: RecordTypes;
|
|
12
|
-
endpoint: string;
|
|
13
|
-
portal?: {
|
|
14
|
-
name: string;
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
interface StreamOptions {
|
|
18
|
-
abortSignal?: AbortSignal;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* A class representing a field in a record.
|
|
22
|
-
*
|
|
23
|
-
* @template T - The type of the field value.
|
|
24
|
-
*/
|
|
25
|
-
export declare class Field<T extends FieldValue> {
|
|
26
|
-
#private;
|
|
27
|
-
parent: Parentable;
|
|
28
|
-
id: string;
|
|
29
|
-
protected _value: T | null;
|
|
30
|
-
static firstContainerDownload: Promise<any> | null;
|
|
31
|
-
constructor(record: Parentable, id: string, originalContents: T);
|
|
32
|
-
/** `true` if this field has changed since it was last loaded or saved. */
|
|
33
|
-
get edited(): boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Resets the 'edited' state of this field, without changing its value
|
|
36
|
-
*/
|
|
37
|
-
updateOriginalContents(): void;
|
|
38
|
-
/**
|
|
39
|
-
* Sets the value of the field.
|
|
40
|
-
*
|
|
41
|
-
* @param {T | null} content - The new content value to be set. Can be either the type T or null.
|
|
42
|
-
* @throws {Error} Cannot set container value using set(). Use upload() instead, if the result is a 'container'.
|
|
43
|
-
*/
|
|
44
|
-
set(content: T | null): void;
|
|
45
|
-
/** Returns the FileMaker metadata for this field. */
|
|
46
|
-
get metadata(): z.infer<typeof ApiFieldMetadata>;
|
|
47
|
-
/** Gets or sets the current field value. */
|
|
48
|
-
get value(): T | null;
|
|
49
|
-
set value(value: T);
|
|
50
|
-
/** Returns this field value as a string. Throws if the value is not a string. */
|
|
51
|
-
get string(): string;
|
|
52
|
-
/**
|
|
53
|
-
* Uploads a file to the container field.
|
|
54
|
-
*
|
|
55
|
-
* @param {Buffer} file - The file content as a buffer.
|
|
56
|
-
*
|
|
57
|
-
* @throws {Error} - Cannot upload a file to the field if it's not a container field.
|
|
58
|
-
* @throws {Error} - Upload failed with HTTP error.
|
|
59
|
-
*
|
|
60
|
-
* @returns {Promise<void>} - A promise that resolves when the file is successfully uploaded.
|
|
61
|
-
*/
|
|
62
|
-
upload(file: File): Promise<void>;
|
|
63
|
-
stream(): Promise<{
|
|
64
|
-
data: Readable;
|
|
65
|
-
mime: string;
|
|
66
|
-
}>;
|
|
67
|
-
/**
|
|
68
|
-
* Returns the container download as a Web `Response`.
|
|
69
|
-
*/
|
|
70
|
-
webStream(options: StreamOptions): Promise<Response>;
|
|
71
|
-
/** Downloads the full container contents into memory. */
|
|
72
|
-
arrayBuffer(): Promise<{
|
|
73
|
-
data: ArrayBuffer;
|
|
74
|
-
mime: string;
|
|
75
|
-
}>;
|
|
76
|
-
}
|
|
77
|
-
export {};
|