@tailor-platform/sdk 2.2.0 → 2.3.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/application-BZfk4HKm.mjs +3 -0
  3. package/dist/{application-BpLeawBg.mjs → application-F-nl107y.mjs} +118 -24
  4. package/dist/application-F-nl107y.mjs.map +1 -0
  5. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +43 -20
  6. package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
  7. package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
  8. package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +1 -1
  9. package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +26 -7
  10. package/dist/cli/lib.mjs +2 -2
  11. package/dist/cli/main.mjs +203 -42
  12. package/dist/cli/main.mjs.map +1 -1
  13. package/dist/cli/ts-hook.mjs +52 -7
  14. package/dist/completion/zsh-worker.zsh +23 -3
  15. package/dist/configure/index.d.mts +2 -2
  16. package/dist/configure/index.mjs +90 -23
  17. package/dist/configure/index.mjs.map +1 -1
  18. package/dist/configure/services/index.d.mts +2 -2
  19. package/dist/configure/services/workflow/index.d.mts +2 -2
  20. package/dist/configure/services/workflow/wait-point.d.mts +72 -13
  21. package/dist/{errors-CWj21238.mjs → errors-BVb6vYGy.mjs} +16 -2
  22. package/dist/{errors-CWj21238.mjs.map → errors-BVb6vYGy.mjs.map} +1 -1
  23. package/dist/kysely/index.d.mts +2 -1
  24. package/dist/kysely/index.mjs.map +1 -1
  25. package/dist/{register-ts-hook-CTth1eqj.mjs → register-ts-hook-ClI226n2.mjs} +2034 -511
  26. package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
  27. package/dist/{service-goqlJJgA.mjs → service-C_WpbKHu.mjs} +38 -4
  28. package/dist/service-C_WpbKHu.mjs.map +1 -0
  29. package/dist/service-D1RCdzIL.mjs +3 -0
  30. package/dist/{service-CCwl3Avt.mjs → service-hZskxZmg.mjs} +2 -2
  31. package/dist/{service-CCwl3Avt.mjs.map → service-hZskxZmg.mjs.map} +1 -1
  32. package/dist/vitest/index.mjs +83 -1
  33. package/dist/vitest/index.mjs.map +1 -1
  34. package/dist/vitest/mocks/file.d.mts +1 -1
  35. package/dist/vitest/mocks/workflow.d.mts +13 -1
  36. package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
  37. package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
  38. package/dist/wait-point-registry-TL99zotw.mjs +47 -0
  39. package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
  40. package/docs/cli/setup.md +14 -1
  41. package/docs/cli/tailordb.md +9 -6
  42. package/docs/cli-reference.md +2 -1
  43. package/docs/github-actions.md +73 -28
  44. package/docs/migration/v2.md +54 -0
  45. package/docs/services/tailordb-migration.md +137 -35
  46. package/docs/services/workflow.md +52 -2
  47. package/docs/testing.md +12 -0
  48. package/package.json +4 -4
  49. package/dist/application-BpLeawBg.mjs.map +0 -1
  50. package/dist/application-BxLLiLsr.mjs +0 -3
  51. package/dist/register-ts-hook-CTth1eqj.mjs.map +0 -1
  52. package/dist/service-_XmjSEGr.mjs +0 -3
  53. package/dist/service-goqlJJgA.mjs.map +0 -1
  54. package/dist/test-env-key-D7UkZp99.mjs +0 -75
  55. package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
@@ -4,7 +4,7 @@ import { SnapshotFieldConfig, SnapshotGqlPermission, SnapshotIndexConfig, Snapsh
4
4
  * Properties shared by all diff change variants
5
5
  */
6
6
  interface DiffChangeBase {
7
- typeName: string;
7
+ tableName: string;
8
8
  reason?: string;
9
9
  }
10
10
  /**
@@ -29,29 +29,40 @@ interface SnapshotPermissionState {
29
29
  recordPermission?: SnapshotRecordPermission;
30
30
  gqlPermission?: SnapshotGqlPermission;
31
31
  }
32
- /** A new type was added to the schema. */
33
- interface TypeAddedChange extends DiffChangeBase {
34
- kind: "type_added";
32
+ /** A new table was added to the schema. */
33
+ interface TableAddedChange extends DiffChangeBase {
34
+ kind: "table_added";
35
35
  after: TailorDBSnapshotType;
36
36
  }
37
- /** An existing type was removed from the schema. */
38
- interface TypeRemovedChange extends DiffChangeBase {
39
- kind: "type_removed";
37
+ /** An existing table was removed from the schema. */
38
+ interface TableRemovedChange extends DiffChangeBase {
39
+ kind: "table_removed";
40
40
  before: TailorDBSnapshotType;
41
41
  }
42
42
  /**
43
- * Legacy type-level settings change. Kept for backward compatibility with
43
+ * A table was renamed. Recorded when the user confirms that a removed + added
44
+ * table pair is a rename (interactively or via `--rename`).
45
+ * `tableName` is the new name; `previousTableName` is the old name.
46
+ */
47
+ interface TableRenamedChange extends DiffChangeBase {
48
+ kind: "table_renamed";
49
+ previousTableName: string;
50
+ before: TailorDBSnapshotType;
51
+ after: TailorDBSnapshotType;
52
+ }
53
+ /**
54
+ * Legacy table-level settings change. Kept for backward compatibility with
44
55
  * diff.json files written by older SDK versions; `before`/`after` may be
45
56
  * absent in those files, hence optional.
46
57
  */
47
- interface TypeModifiedChange extends DiffChangeBase {
48
- kind: "type_modified";
58
+ interface TableModifiedChange extends DiffChangeBase {
59
+ kind: "table_modified";
49
60
  before?: TypeSettingsPatch;
50
61
  after?: TypeSettingsPatch;
51
62
  }
52
- /** Type-level settings or metadata changed. */
53
- interface TypeSettingsModifiedChange extends DiffChangeBase {
54
- kind: "type_settings_modified";
63
+ /** Table-level settings or metadata changed. */
64
+ interface TableSettingsModifiedChange extends DiffChangeBase {
65
+ kind: "table_settings_modified";
55
66
  before: SnapshotTypeSettingsState;
56
67
  after: SnapshotTypeSettingsState;
57
68
  }
@@ -74,6 +85,18 @@ interface FieldModifiedChange extends DiffChangeBase {
74
85
  before: SnapshotFieldConfig;
75
86
  after: SnapshotFieldConfig;
76
87
  }
88
+ /**
89
+ * A field was renamed within a type. Recorded when the user confirms that a
90
+ * removed + added field pair is a rename (interactively or via `--rename`).
91
+ * `fieldName` is the new name; `previousFieldName` is the old name.
92
+ */
93
+ interface FieldRenamedChange extends DiffChangeBase {
94
+ kind: "field_renamed";
95
+ fieldName: string;
96
+ previousFieldName: string;
97
+ before: SnapshotFieldConfig;
98
+ after: SnapshotFieldConfig;
99
+ }
77
100
  /** A field type changed and must remain on the previous type until Post-phase. */
78
101
  interface FieldTypeModifiedChange extends DiffChangeBase {
79
102
  kind: "field_type_modified";
@@ -163,9 +186,9 @@ interface TypeScriptsState {
163
186
  };
164
187
  typeValidateExpr?: string;
165
188
  }
166
- /** Type-level hook/validate scripts changed. */
167
- interface TypeScriptsModifiedChange extends DiffChangeBase {
168
- kind: "type_scripts_modified";
189
+ /** Table-level hook/validate scripts changed. */
190
+ interface TableScriptsModifiedChange extends DiffChangeBase {
191
+ kind: "table_scripts_modified";
169
192
  before: TypeScriptsState;
170
193
  after: TypeScriptsState;
171
194
  }
@@ -173,7 +196,7 @@ interface TypeScriptsModifiedChange extends DiffChangeBase {
173
196
  * Single change in migration diff, discriminated by `kind` so that
174
197
  * `before`/`after` are typed per change kind.
175
198
  */
176
- type DiffChange = TypeAddedChange | TypeRemovedChange | TypeModifiedChange | TypeSettingsModifiedChange | FieldAddedChange | FieldRemovedChange | FieldModifiedChange | FieldTypeModifiedChange | IndexAddedChange | IndexRemovedChange | IndexModifiedChange | FileAddedChange | FileRemovedChange | FileModifiedChange | RelationshipAddedChange | RelationshipRemovedChange | RelationshipModifiedChange | PermissionModifiedChange | TypeScriptsModifiedChange;
199
+ type DiffChange = TableAddedChange | TableRemovedChange | TableRenamedChange | TableModifiedChange | TableSettingsModifiedChange | FieldAddedChange | FieldRemovedChange | FieldModifiedChange | FieldRenamedChange | FieldTypeModifiedChange | IndexAddedChange | IndexRemovedChange | IndexModifiedChange | FileAddedChange | FileRemovedChange | FileModifiedChange | RelationshipAddedChange | RelationshipRemovedChange | RelationshipModifiedChange | PermissionModifiedChange | TableScriptsModifiedChange;
177
200
  /**
178
201
  * Migration diff - changes between two schema versions
179
202
  * Stored as XXXX/diff.json (e.g., 0001/diff.json)
@@ -210,7 +233,7 @@ interface ScriptSkippedInfo {
210
233
  * Breaking change information in migration diff
211
234
  */
212
235
  interface BreakingChangeInfo {
213
- typeName: string;
236
+ tableName: string;
214
237
  fieldName?: string;
215
238
  reason: string;
216
239
  /** If true, this change is not supported and migration generation will fail */
@@ -227,7 +250,7 @@ interface BreakingChangeInfo {
227
250
  * preserve or transform data before the change applies.
228
251
  */
229
252
  interface WarningChangeInfo {
230
- typeName: string;
253
+ tableName: string;
231
254
  fieldName?: string;
232
255
  reason: string;
233
256
  }
@@ -250,4 +273,4 @@ declare function formatMigrationDiff(diff: MigrationDiff): string;
250
273
  */
251
274
  declare function formatDiffSummary(diff: MigrationDiff): string;
252
275
  //#endregion
253
- export { BreakingChangeInfo, DiffChange, FieldAddedChange, FieldModifiedChange, FieldRemovedChange, FieldTypeModifiedChange, FileAddedChange, FileModifiedChange, FileRemovedChange, IndexAddedChange, IndexModifiedChange, IndexRemovedChange, MigrationDiff, PermissionModifiedChange, RelationshipAddedChange, RelationshipModifiedChange, RelationshipRemovedChange, ScriptSkippedInfo, SnapshotPermissionState, SnapshotTypeSettingsState, TypeAddedChange, TypeModifiedChange, TypeRemovedChange, TypeScriptsModifiedChange, TypeScriptsState, TypeSettingsModifiedChange, TypeSettingsPatch, WarningChangeInfo, formatDiffSummary, formatMigrationDiff, hasChanges };
276
+ export { BreakingChangeInfo, DiffChange, FieldAddedChange, FieldModifiedChange, FieldRemovedChange, FieldRenamedChange, FieldTypeModifiedChange, FileAddedChange, FileModifiedChange, FileRemovedChange, IndexAddedChange, IndexModifiedChange, IndexRemovedChange, MigrationDiff, PermissionModifiedChange, RelationshipAddedChange, RelationshipModifiedChange, RelationshipRemovedChange, ScriptSkippedInfo, SnapshotPermissionState, SnapshotTypeSettingsState, TableAddedChange, TableModifiedChange, TableRemovedChange, TableRenamedChange, TableScriptsModifiedChange, TableSettingsModifiedChange, TypeScriptsState, TypeSettingsPatch, WarningChangeInfo, formatDiffSummary, formatMigrationDiff, hasChanges };
@@ -5,6 +5,12 @@ interface GenerateOptions {
5
5
  name?: string;
6
6
  yes?: boolean;
7
7
  init?: boolean;
8
+ /** `--rename Type.old:new` / `--rename OldType:NewType` values confirming renames non-interactively. */
9
+ renames?: string[];
10
+ /** `--drop Type.field` / `--drop Type` values confirming removals non-interactively. */
11
+ drops?: string[];
12
+ /** `--expand-contract Type.field` values approving a field type conversion. */
13
+ expandContracts?: string[];
8
14
  }
9
15
  /**
10
16
  * Generate migration files for TailorDB schema changes
@@ -0,0 +1,22 @@
1
+ //#region src/cli/commands/tailordb/migrate/rename-detection.d.ts
2
+ /**
3
+ * A confirmed field rename to record in the migration diff.
4
+ */
5
+ interface FieldRenameSpec {
6
+ tableName: string;
7
+ /** Field name before the rename. */
8
+ previousFieldName: string;
9
+ /** Field name after the rename. */
10
+ fieldName: string;
11
+ }
12
+ /**
13
+ * A confirmed type rename to record in the migration diff.
14
+ */
15
+ interface TypeRenameSpec {
16
+ /** Type name before the rename. */
17
+ previousTableName: string;
18
+ /** Type name after the rename. */
19
+ tableName: string;
20
+ }
21
+ //#endregion
22
+ export { FieldRenameSpec, TypeRenameSpec };
@@ -198,7 +198,7 @@ interface SchemaSnapshot {
198
198
  version: number;
199
199
  namespace: string;
200
200
  createdAt: string;
201
- types: Record<string, TailorDBSnapshotType>;
201
+ tables: Record<string, TailorDBSnapshotType>;
202
202
  rebaseline?: RebaselineMarker;
203
203
  }
204
204
  declare const normalizedSchemaSnapshotBrand: unique symbol;
@@ -2,6 +2,7 @@ import { TailorDBType } from "../../../../parser/service/tailordb/types.mjs";
2
2
  import "../../../../tailor-proto/src/tailor/v1/tailordb_resource_pb.mjs";
3
3
  import { NormalizedSchemaSnapshot, RebaselineMarker, SchemaSnapshot, SnapshotActionPermission, SnapshotEnumValue, SnapshotFieldConfig, SnapshotGqlPermission, SnapshotGqlPermissionPolicy, SnapshotIndexConfig, SnapshotPermissionCondition, SnapshotPermissionOperand, SnapshotRecordPermission, SnapshotRelationship, TailorDBSnapshotType } from "./snapshot-types.mjs";
4
4
  import { MigrationDiff } from "./diff-calculator.mjs";
5
+ import { FieldRenameSpec, TypeRenameSpec } from "./rename-detection.mjs";
5
6
  //#region src/cli/commands/tailordb/migrate/snapshot.d.ts
6
7
  /**
7
8
  * Initial schema migration number (0000)
@@ -78,17 +79,35 @@ declare function reconstructSnapshotFromMigrations(migrationsDir: string, maxVer
78
79
  */
79
80
  declare function getLatestMigrationNumber(migrationsDir: string): number;
80
81
  /**
81
- * Compare two snapshots and generate a diff
82
- * @param {SchemaSnapshot} previous - Previous schema snapshot
83
- * @param {SchemaSnapshot} current - Current schema snapshot
82
+ * Options for {@link compareSnapshots}.
83
+ */
84
+ interface CompareSnapshotsOptions {
85
+ /**
86
+ * Confirmed field renames. Each spec replaces the corresponding
87
+ * `field_removed` + `field_added` pair with a single breaking
88
+ * `field_renamed` change. Specs are validated against both snapshots.
89
+ */
90
+ fieldRenames?: readonly FieldRenameSpec[];
91
+ /**
92
+ * Confirmed type renames. Each spec replaces the corresponding
93
+ * `table_removed` + `table_added` pair with a single breaking
94
+ * `table_renamed` change. Specs are validated against both snapshots.
95
+ */
96
+ typeRenames?: readonly TypeRenameSpec[];
97
+ }
98
+ /**
99
+ * Compare two normalized snapshots and generate a diff
100
+ * @param {NormalizedSchemaSnapshot} previous - Previous normalized snapshot
101
+ * @param {NormalizedSchemaSnapshot} current - Current normalized snapshot
102
+ * @param {CompareSnapshotsOptions} [options] - Comparison options
84
103
  * @returns {MigrationDiff} Migration diff between snapshots
85
104
  */
86
- declare function compareSnapshots(previous: SchemaSnapshot, current: SchemaSnapshot): MigrationDiff;
105
+ declare function compareSnapshots(previous: NormalizedSchemaSnapshot, current: NormalizedSchemaSnapshot, options?: CompareSnapshotsOptions): MigrationDiff;
87
106
  /**
88
107
  * Compare a snapshot against canonical TailorDBSnapshotType-shaped local types.
89
108
  * Callers are expected to pre-convert TailorDBService.types to TailorDBSnapshotType via
90
- * `createSnapshotType`. As a safety net, `compareSnapshots` re-runs idempotent
91
- * normalization on both sides, so a caller that forgets will still get correct
109
+ * `createSnapshotType`. As a safety net, both sides are re-run through idempotent
110
+ * normalization here, so a caller that forgets will still get correct
92
111
  * comparisons (no silent false drift).
93
112
  * @param {SchemaSnapshot} snapshot - Schema snapshot to compare against
94
113
  * @param {Record<string, TailorDBSnapshotType>} localTypes - Local snapshot-shaped types
@@ -97,4 +116,4 @@ declare function compareSnapshots(previous: SchemaSnapshot, current: SchemaSnaps
97
116
  */
98
117
  declare function compareLocalTypesWithSnapshot(snapshot: SchemaSnapshot, localTypes: Record<string, TailorDBSnapshotType>, namespace: string): MigrationDiff;
99
118
  //#endregion
100
- export { DB_TYPES_FILE_NAME, DIFF_FILE_NAME, INITIAL_SCHEMA_NUMBER, MIGRATE_FILE_NAME, MIGRATE_TEST_FILE_NAME, MigrationFileType, type NormalizedSchemaSnapshot, SCHEMA_FILE_NAME, type SchemaSnapshot, type SnapshotFieldConfig, type TailorDBSnapshotType, compareLocalTypesWithSnapshot, compareSnapshots, createSnapshotFromLocalTypes, getLatestMigrationNumber, getMigrationDirPath, getMigrationFilePath, getMigrationFiles, getNextMigrationNumber, reconstructSnapshotFromMigrations };
119
+ export { CompareSnapshotsOptions, DB_TYPES_FILE_NAME, DIFF_FILE_NAME, INITIAL_SCHEMA_NUMBER, MIGRATE_FILE_NAME, MIGRATE_TEST_FILE_NAME, MigrationFileType, type NormalizedSchemaSnapshot, SCHEMA_FILE_NAME, type SchemaSnapshot, type SnapshotFieldConfig, type TailorDBSnapshotType, compareLocalTypesWithSnapshot, compareSnapshots, createSnapshotFromLocalTypes, getLatestMigrationNumber, getMigrationDirPath, getMigrationFilePath, getMigrationFiles, getNextMigrationNumber, reconstructSnapshotFromMigrations };
package/dist/cli/lib.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { $n as formatDiffSummary, $t as listWorkspaces, A as truncate, At as triggerExecutor, Bn as getMigrationDirPath, D as resumeWorkflow, Dn as INITIAL_SCHEMA_NUMBER, Dr as loadWorkspaceId, Dt as listWebhookExecutors, En as DIFF_FILE_NAME, Et as getFunctionRegistry, F as chunkSeedData, Fn as compareLocalTypesWithSnapshot, Hn as getMigrationFiles, I as bundleSeedScript, In as compareSnapshots, It as listExecutorJobs, J as remove, Jt as deploy, L as generate$1, Ln as createSnapshotFromLocalTypes, Lt as watchExecutorJob, M as loadSeedContext, Mt as listExecutors, Nt as getExecutorJob, On as MIGRATE_FILE_NAME, P as loadTailorDBNamespaces, Pt as getExecutorWaitFailureMessage, Q as organizationTree, S as getAppHealth, St as generate, T as waitWorkflowExecution, Tn as DB_TYPES_FILE_NAME, U as deployStaticWebsite, Un as getNextMigrationNumber, Ut as getWorkflowExecution, Vn as getMigrationFilePath, Vt as getWorkflow, W as show, Wt as listWorkflowExecutions, Yn as MIGRATION_LABEL_KEY, Z as updateOrganization, _ as getWorkspace, _t as getOAuth2Client, at as updateFolder, b as listApps, cn as executeScript, d as listUsers, dn as bundleMigrationScript, dt as deleteFolder, er as formatMigrationDiff, gr as loadConfig, h as restoreWorkspace, hr as assertWritable, ht as listOAuth2Clients, i as query, jn as SCHEMA_FILE_NAME, k as listWorkflows, kn as MIGRATE_TEST_FILE_NAME, l as removeUser, ln as waitForExecution, lt as getFolder, mr as apiCall, n as errorToJson, nn as createWorkspace, nr as getNamespacesWithMigrations, p as inviteUser, pt as createFolder, qn as reconstructSnapshotFromMigrations, qt as getExecutor, r as serializeError, rt as getOrganization, s as updateUser, st as listFolders, t as registerTsHook, tr as hasChanges, tt as listOrganizations, ur as generateUserTypes, vt as getMachineUserToken, wt as listFunctionRegistries, xr as loadAccessToken, xt as listMachineUsers, y as deleteWorkspace, zn as getLatestMigrationNumber, zt as startWorkflow } from "../register-ts-hook-CTth1eqj.mjs";
2
- import { K as initOperatorClient } from "../application-BpLeawBg.mjs";
1
+ import { $t as listWorkspaces, A as truncate, At as triggerExecutor, Bn as getLatestMigrationNumber, D as resumeWorkflow, Dn as INITIAL_SCHEMA_NUMBER, Dt as listWebhookExecutors, En as DIFF_FILE_NAME, Et as getFunctionRegistry, F as chunkSeedData, Hn as getMigrationFilePath, I as bundleSeedScript, In as compareLocalTypesWithSnapshot, It as listExecutorJobs, J as remove, Jn as reconstructSnapshotFromMigrations, Jt as deploy, L as generate$1, Ln as compareSnapshots, Lt as watchExecutorJob, M as loadSeedContext, Mn as SCHEMA_FILE_NAME, Mt as listExecutors, Nt as getExecutorJob, On as MIGRATE_FILE_NAME, Or as loadWorkspaceId, P as loadTailorDBNamespaces, Pt as getExecutorWaitFailureMessage, Q as organizationTree, Rn as createSnapshotFromLocalTypes, S as getAppHealth, Sr as loadAccessToken, St as generate, T as waitWorkflowExecution, Tn as DB_TYPES_FILE_NAME, U as deployStaticWebsite, Un as getMigrationFiles, Ut as getWorkflowExecution, Vn as getMigrationDirPath, Vt as getWorkflow, W as show, Wn as getNextMigrationNumber, Wt as listWorkflowExecutions, Xn as MIGRATION_LABEL_KEY, Z as updateOrganization, _ as getWorkspace, _r as loadConfig, _t as getOAuth2Client, at as updateFolder, b as listApps, cn as executeScript, d as listUsers, dn as bundleMigrationScript, dr as generateUserTypes, dt as deleteFolder, er as formatDiffSummary, gr as assertWritable, h as restoreWorkspace, hr as apiCall, ht as listOAuth2Clients, i as query, k as listWorkflows, kn as MIGRATE_TEST_FILE_NAME, l as removeUser, ln as waitForExecution, lt as getFolder, n as errorToJson, nn as createWorkspace, nr as hasChanges, p as inviteUser, pt as createFolder, qt as getExecutor, r as serializeError, rr as getNamespacesWithMigrations, rt as getOrganization, s as updateUser, st as listFolders, t as registerTsHook, tr as formatMigrationDiff, tt as listOrganizations, vt as getMachineUserToken, wt as listFunctionRegistries, xt as listMachineUsers, y as deleteWorkspace, zt as startWorkflow } from "../register-ts-hook-ClI226n2.mjs";
2
+ import { J as initOperatorClient } from "../application-F-nl107y.mjs";
3
3
  import { t as isPluginGeneratedType } from "../type-source-DH_LH20p.mjs";
4
4
 
5
5
  //#region src/cli/lib.ts