@useairfoil/producer-linear 0.0.1 → 0.0.3

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 (51) hide show
  1. package/LICENSE.txt +201 -0
  2. package/README.md +71 -0
  3. package/dist/index.d.mts +4027 -0
  4. package/dist/index.mjs +3 -0
  5. package/dist/main.d.mts +1 -0
  6. package/dist/main.mjs +1305 -0
  7. package/dist/main.mjs.map +1 -0
  8. package/dist/manifest.d.mts +114 -0
  9. package/dist/manifest.mjs +161 -0
  10. package/dist/manifest.mjs.map +1 -0
  11. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  12. package/dist/src-BbzySPR7.mjs +1688 -0
  13. package/dist/src-BbzySPR7.mjs.map +1 -0
  14. package/package.json +46 -8
  15. package/src/client/client.ts +200 -0
  16. package/src/connector.ts +151 -0
  17. package/src/create-table.ts +10 -0
  18. package/src/demo/index.ts +137 -0
  19. package/src/demo/initiatives.ts +116 -0
  20. package/src/demo/issues.ts +320 -0
  21. package/src/demo/projects.ts +215 -0
  22. package/src/demo/setup.ts +151 -0
  23. package/src/demo/shared.ts +122 -0
  24. package/src/demo/teams.ts +173 -0
  25. package/src/index.ts +83 -0
  26. package/src/main.ts +28 -0
  27. package/src/manifest.ts +58 -0
  28. package/src/resources/comments.ts +32 -0
  29. package/src/resources/customers.ts +105 -0
  30. package/src/resources/documents.ts +31 -0
  31. package/src/resources/initiatives.ts +136 -0
  32. package/src/resources/issue-history.ts +119 -0
  33. package/src/resources/issues.ts +131 -0
  34. package/src/resources/pages.ts +264 -0
  35. package/src/resources/projects.ts +161 -0
  36. package/src/resources/releases.ts +114 -0
  37. package/src/resources/teams.ts +140 -0
  38. package/src/sandbox.ts +35 -0
  39. package/src/schemas/comments.ts +52 -0
  40. package/src/schemas/customers.ts +96 -0
  41. package/src/schemas/documents.ts +40 -0
  42. package/src/schemas/initiatives.ts +155 -0
  43. package/src/schemas/issues.ts +262 -0
  44. package/src/schemas/projects.ts +210 -0
  45. package/src/schemas/releases.ts +132 -0
  46. package/src/schemas/shared.ts +68 -0
  47. package/src/schemas/teams.ts +177 -0
  48. package/src/start.ts +47 -0
  49. package/src/tables.ts +83 -0
  50. package/src/webhook/route.ts +50 -0
  51. package/src/webhook/signature.ts +28 -0
@@ -0,0 +1,161 @@
1
+ import { Resource } from "@useairfoil/connector-kit";
2
+
3
+ import type { LinearClientService } from "../client/client";
4
+
5
+ import {
6
+ ProjectLabelObjectSchema,
7
+ ProjectLabelSchema,
8
+ ProjectMilestoneObjectSchema,
9
+ ProjectMilestoneSchema,
10
+ ProjectObjectSchema,
11
+ ProjectRelationObjectSchema,
12
+ ProjectRelationSchema,
13
+ ProjectSchema,
14
+ ProjectStatusObjectSchema,
15
+ ProjectStatusSchema,
16
+ ProjectUpdateObjectSchema,
17
+ ProjectUpdateSchema,
18
+ } from "../schemas/projects";
19
+ import {
20
+ backfillList,
21
+ changesList,
22
+ checkList,
23
+ listSources,
24
+ listSpec,
25
+ rowsOf,
26
+ webhookList,
27
+ } from "./pages";
28
+
29
+ const projectSpec = listSpec({
30
+ connection: "projects",
31
+ single: "project",
32
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
33
+ name description content url icon color status_id: status { id } priority
34
+ priority_label: priorityLabel health health_updated_at: healthUpdatedAt progress scope
35
+ lead_id: lead { id } creator_id: creator { id }
36
+ converted_from_issue_id: convertedFromIssue { id } start_date: startDate
37
+ target_date: targetDate started_at: startedAt completed_at: completedAt
38
+ canceled_at: canceledAt auto_archived_at: autoArchivedAt label_ids: labelIds
39
+ team_ids: teams(first: 50) { nodes { id } } member_ids: members(first: 100) { nodes { id } }
40
+ trashed`,
41
+ item: ProjectObjectSchema,
42
+ // Each project reads up to 150 teams and members, so pages stay under the cost limit.
43
+ first: 50,
44
+ updatedAt: (project) => project.updated_at,
45
+ rows: rowsOf((project) => ({ ...project, version: project.updated_at, _deleted: false })),
46
+ });
47
+
48
+ export const makeProjects = (client: LinearClientService) =>
49
+ Resource.entity({
50
+ name: "projects",
51
+ rowSchema: ProjectSchema,
52
+ version: "version",
53
+
54
+ check: checkList(client, projectSpec),
55
+ backfill: backfillList(client, projectSpec),
56
+ changes: changesList(client, projectSpec),
57
+ webhook: webhookList(client, projectSpec),
58
+ });
59
+
60
+ const projectMilestoneSpec = listSpec({
61
+ connection: "projectMilestones",
62
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
63
+ name description project_id: project { id } target_date: targetDate status progress
64
+ sort_order: sortOrder`,
65
+ item: ProjectMilestoneObjectSchema,
66
+ updatedAt: (milestone) => milestone.updated_at,
67
+ rows: rowsOf((milestone) => ({ ...milestone, version: milestone.updated_at })),
68
+ });
69
+
70
+ export const makeProjectMilestones = (client: LinearClientService) =>
71
+ Resource.entity({
72
+ name: "project_milestones",
73
+ rowSchema: ProjectMilestoneSchema,
74
+ version: "version",
75
+
76
+ check: checkList(client, projectMilestoneSpec),
77
+ backfill: backfillList(client, projectMilestoneSpec),
78
+ changes: changesList(client, projectMilestoneSpec),
79
+ });
80
+
81
+ const projectUpdateSpec = listSpec({
82
+ connection: "projectUpdates",
83
+ single: "projectUpdate",
84
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
85
+ project_id: project { id } user_id: user { id } body health url edited_at: editedAt
86
+ comment_count: commentCount`,
87
+ item: ProjectUpdateObjectSchema,
88
+ updatedAt: (update) => update.updated_at,
89
+ rows: rowsOf((update) => ({ ...update, version: update.updated_at, _deleted: false })),
90
+ });
91
+
92
+ export const makeProjectUpdates = (client: LinearClientService) =>
93
+ Resource.entity({
94
+ name: "project_updates",
95
+ rowSchema: ProjectUpdateSchema,
96
+ version: "version",
97
+
98
+ check: checkList(client, projectUpdateSpec),
99
+ backfill: backfillList(client, projectUpdateSpec),
100
+ changes: changesList(client, projectUpdateSpec),
101
+ webhook: webhookList(client, projectUpdateSpec),
102
+ });
103
+
104
+ const projectStatusSpec = listSpec({
105
+ connection: "projectStatuses",
106
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color description position`,
107
+ item: ProjectStatusObjectSchema,
108
+ updatedAt: (status) => status.updated_at,
109
+ rows: rowsOf((status) => ({ ...status, version: status.updated_at })),
110
+ });
111
+
112
+ export const makeProjectStatuses = (client: LinearClientService) =>
113
+ Resource.entity({
114
+ name: "project_statuses",
115
+ rowSchema: ProjectStatusSchema,
116
+ version: "version",
117
+ ...listSources(client, projectStatusSpec),
118
+ });
119
+
120
+ const projectLabelSpec = listSpec({
121
+ connection: "projectLabels",
122
+ single: "projectLabel",
123
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
124
+ name description color is_group: isGroup parent_id: parent { id }
125
+ creator_id: creator { id } retired_at: retiredAt`,
126
+ item: ProjectLabelObjectSchema,
127
+ updatedAt: (label) => label.updated_at,
128
+ rows: rowsOf((label) => ({ ...label, version: label.updated_at, _deleted: false })),
129
+ });
130
+
131
+ export const makeProjectLabels = (client: LinearClientService) =>
132
+ Resource.entity({
133
+ name: "project_labels",
134
+ rowSchema: ProjectLabelSchema,
135
+ version: "version",
136
+ ...listSources(client, projectLabelSpec),
137
+ webhook: webhookList(client, projectLabelSpec),
138
+ });
139
+
140
+ const projectRelationSpec = listSpec({
141
+ connection: "projectRelations",
142
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
143
+ type project_id: project { id } project_milestone_id: projectMilestone { id }
144
+ anchor_type: anchorType related_project_id: relatedProject { id }
145
+ related_project_milestone_id: relatedProjectMilestone { id }
146
+ related_anchor_type: relatedAnchorType user_id: user { id }`,
147
+ item: ProjectRelationObjectSchema,
148
+ updatedAt: (relation) => relation.updated_at,
149
+ rows: rowsOf((relation) => ({ ...relation, version: relation.updated_at })),
150
+ });
151
+
152
+ export const makeProjectRelations = (client: LinearClientService) =>
153
+ Resource.entity({
154
+ name: "project_relations",
155
+ rowSchema: ProjectRelationSchema,
156
+ version: "version",
157
+
158
+ check: checkList(client, projectRelationSpec),
159
+ backfill: backfillList(client, projectRelationSpec),
160
+ changes: changesList(client, projectRelationSpec),
161
+ });
@@ -0,0 +1,114 @@
1
+ import { Resource } from "@useairfoil/connector-kit";
2
+
3
+ import type { LinearClientService } from "../client/client";
4
+
5
+ import {
6
+ IssueToReleaseObjectSchema,
7
+ IssueToReleaseSchema,
8
+ ReleaseNoteObjectSchema,
9
+ ReleaseNoteSchema,
10
+ ReleaseObjectSchema,
11
+ ReleasePipelineObjectSchema,
12
+ ReleasePipelineSchema,
13
+ ReleaseSchema,
14
+ ReleaseStageObjectSchema,
15
+ ReleaseStageSchema,
16
+ } from "../schemas/releases";
17
+ import { backfillList, changesList, checkList, listSources, listSpec, rowsOf } from "./pages";
18
+
19
+ const releaseSpec = listSpec({
20
+ connection: "releases",
21
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
22
+ name description release_version: version url commit_sha: commitSha
23
+ pipeline_id: pipeline { id } stage_id: stage { id } creator_id: creator { id }
24
+ start_date: startDate target_date: targetDate started_at: startedAt
25
+ completed_at: completedAt canceled_at: canceledAt trashed`,
26
+ item: ReleaseObjectSchema,
27
+ updatedAt: (release) => release.updated_at,
28
+ rows: rowsOf((release) => ({ ...release, version: release.updated_at })),
29
+ });
30
+
31
+ export const makeReleases = (client: LinearClientService) =>
32
+ Resource.entity({
33
+ name: "releases",
34
+ rowSchema: ReleaseSchema,
35
+ version: "version",
36
+
37
+ check: checkList(client, releaseSpec),
38
+ backfill: backfillList(client, releaseSpec),
39
+ changes: changesList(client, releaseSpec),
40
+ });
41
+
42
+ const releaseNoteSpec = listSpec({
43
+ connection: "releaseNotes",
44
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
45
+ title content: documentContent { content } url pipeline_id: pipeline { id }
46
+ release_ids: releases { id }`,
47
+ item: ReleaseNoteObjectSchema,
48
+ updatedAt: (note) => note.updated_at,
49
+ rows: rowsOf((note) => ({ ...note, version: note.updated_at })),
50
+ });
51
+
52
+ export const makeReleaseNotes = (client: LinearClientService) =>
53
+ Resource.entity({
54
+ name: "release_notes",
55
+ rowSchema: ReleaseNoteSchema,
56
+ version: "version",
57
+
58
+ check: checkList(client, releaseNoteSpec),
59
+ backfill: backfillList(client, releaseNoteSpec),
60
+ changes: changesList(client, releaseNoteSpec),
61
+ });
62
+
63
+ const releasePipelineSpec = listSpec({
64
+ connection: "releasePipelines",
65
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
66
+ name type is_production: isProduction url include_path_patterns: includePathPatterns
67
+ approximate_release_count: approximateReleaseCount trashed`,
68
+ item: ReleasePipelineObjectSchema,
69
+ updatedAt: (pipeline) => pipeline.updated_at,
70
+ rows: rowsOf((pipeline) => ({ ...pipeline, version: pipeline.updated_at })),
71
+ });
72
+
73
+ export const makeReleasePipelines = (client: LinearClientService) =>
74
+ Resource.entity({
75
+ name: "release_pipelines",
76
+ rowSchema: ReleasePipelineSchema,
77
+ version: "version",
78
+ ...listSources(client, releasePipelineSpec),
79
+ });
80
+
81
+ const releaseStageSpec = listSpec({
82
+ connection: "releaseStages",
83
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color position frozen pipeline_id: pipeline { id }`,
84
+ item: ReleaseStageObjectSchema,
85
+ updatedAt: (stage) => stage.updated_at,
86
+ rows: rowsOf((stage) => ({ ...stage, version: stage.updated_at })),
87
+ });
88
+
89
+ export const makeReleaseStages = (client: LinearClientService) =>
90
+ Resource.entity({
91
+ name: "release_stages",
92
+ rowSchema: ReleaseStageSchema,
93
+ version: "version",
94
+ ...listSources(client, releaseStageSpec),
95
+ });
96
+
97
+ const issueToReleaseSpec = listSpec({
98
+ connection: "issueToReleases",
99
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt issue_id: issue { id } release_id: release { id }`,
100
+ item: IssueToReleaseObjectSchema,
101
+ updatedAt: (link) => link.updated_at,
102
+ rows: rowsOf((link) => ({ ...link, version: link.updated_at })),
103
+ });
104
+
105
+ export const makeIssueToReleases = (client: LinearClientService) =>
106
+ Resource.entity({
107
+ name: "issue_to_releases",
108
+ rowSchema: IssueToReleaseSchema,
109
+ version: "version",
110
+
111
+ check: checkList(client, issueToReleaseSpec),
112
+ backfill: backfillList(client, issueToReleaseSpec),
113
+ changes: changesList(client, issueToReleaseSpec),
114
+ });
@@ -0,0 +1,140 @@
1
+ import { Resource } from "@useairfoil/connector-kit";
2
+
3
+ import type { LinearClientService } from "../client/client";
4
+
5
+ import {
6
+ CycleObjectSchema,
7
+ CycleSchema,
8
+ ExternalUserObjectSchema,
9
+ ExternalUserSchema,
10
+ TeamMembershipObjectSchema,
11
+ TeamMembershipSchema,
12
+ TeamObjectSchema,
13
+ TeamSchema,
14
+ UserObjectSchema,
15
+ UserSchema,
16
+ WorkflowStateObjectSchema,
17
+ WorkflowStateSchema,
18
+ } from "../schemas/teams";
19
+ import {
20
+ backfillList,
21
+ changesList,
22
+ checkList,
23
+ listSources,
24
+ listSpec,
25
+ rowsOf,
26
+ webhookList,
27
+ } from "./pages";
28
+
29
+ const teamSpec = listSpec({
30
+ connection: "teams",
31
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
32
+ name key display_name: displayName description visibility parent_id: parent { id }
33
+ timezone cycles_enabled: cyclesEnabled cycle_duration: cycleDuration
34
+ cycle_cooldown_time: cycleCooldownTime cycle_start_day: cycleStartDay
35
+ triage_enabled: triageEnabled issue_estimation_type: issueEstimationType
36
+ default_issue_estimate: defaultIssueEstimate auto_archive_period: autoArchivePeriod
37
+ auto_close_period: autoClosePeriod retired_at: retiredAt`,
38
+ item: TeamObjectSchema,
39
+ updatedAt: (team) => team.updated_at,
40
+ rows: rowsOf((team) => ({ ...team, version: team.updated_at })),
41
+ });
42
+
43
+ export const makeTeams = (client: LinearClientService) =>
44
+ Resource.entity({
45
+ name: "teams",
46
+ rowSchema: TeamSchema,
47
+ version: "version",
48
+ ...listSources(client, teamSpec),
49
+ });
50
+
51
+ const teamMembershipSpec = listSpec({
52
+ connection: "teamMemberships",
53
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt team_id: team { id } user_id: user { id } owner`,
54
+ item: TeamMembershipObjectSchema,
55
+ updatedAt: (membership) => membership.updated_at,
56
+ rows: rowsOf((membership) => ({ ...membership, version: membership.updated_at })),
57
+ });
58
+
59
+ export const makeTeamMemberships = (client: LinearClientService) =>
60
+ Resource.entity({
61
+ name: "team_memberships",
62
+ rowSchema: TeamMembershipSchema,
63
+ version: "version",
64
+ ...listSources(client, teamMembershipSpec),
65
+ });
66
+
67
+ const workflowStateSpec = listSpec({
68
+ connection: "workflowStates",
69
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name type color description position team_id: team { id }`,
70
+ item: WorkflowStateObjectSchema,
71
+ updatedAt: (state) => state.updated_at,
72
+ rows: rowsOf((state) => ({ ...state, version: state.updated_at })),
73
+ });
74
+
75
+ export const makeWorkflowStates = (client: LinearClientService) =>
76
+ Resource.entity({
77
+ name: "workflow_states",
78
+ rowSchema: WorkflowStateSchema,
79
+ version: "version",
80
+ ...listSources(client, workflowStateSpec),
81
+ });
82
+
83
+ const userSpec = listSpec({
84
+ connection: "users",
85
+ single: "user",
86
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
87
+ name display_name: displayName email avatar_url: avatarUrl title timezone
88
+ active admin owner guest app last_seen: lastSeen url`,
89
+ item: UserObjectSchema,
90
+ updatedAt: (user) => user.updated_at,
91
+ rows: rowsOf((user) => ({ ...user, version: user.updated_at, _deleted: false })),
92
+ });
93
+
94
+ export const makeUsers = (client: LinearClientService) =>
95
+ Resource.entity({
96
+ name: "users",
97
+ rowSchema: UserSchema,
98
+ version: "version",
99
+ ...listSources(client, userSpec),
100
+ webhook: webhookList(client, userSpec),
101
+ });
102
+
103
+ const externalUserSpec = listSpec({
104
+ connection: "externalUsers",
105
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt name display_name: displayName email avatar_url: avatarUrl last_seen: lastSeen`,
106
+ item: ExternalUserObjectSchema,
107
+ updatedAt: (user) => user.updated_at,
108
+ rows: rowsOf((user) => ({ ...user, version: user.updated_at })),
109
+ });
110
+
111
+ export const makeExternalUsers = (client: LinearClientService) =>
112
+ Resource.entity({
113
+ name: "external_users",
114
+ rowSchema: ExternalUserSchema,
115
+ version: "version",
116
+ ...listSources(client, externalUserSpec),
117
+ });
118
+
119
+ const cycleSpec = listSpec({
120
+ connection: "cycles",
121
+ single: "cycle",
122
+ fields: `id created_at: createdAt updated_at: updatedAt archived_at: archivedAt
123
+ number name description team_id: team { id } starts_at: startsAt ends_at: endsAt
124
+ completed_at: completedAt auto_archived_at: autoArchivedAt progress`,
125
+ item: CycleObjectSchema,
126
+ updatedAt: (cycle) => cycle.updated_at,
127
+ rows: rowsOf((cycle) => ({ ...cycle, version: cycle.updated_at, _deleted: false })),
128
+ });
129
+
130
+ export const makeCycles = (client: LinearClientService) =>
131
+ Resource.entity({
132
+ name: "cycles",
133
+ rowSchema: CycleSchema,
134
+ version: "version",
135
+
136
+ check: checkList(client, cycleSpec),
137
+ backfill: backfillList(client, cycleSpec),
138
+ changes: changesList(client, cycleSpec),
139
+ webhook: webhookList(client, cycleSpec),
140
+ });
package/src/sandbox.ts ADDED
@@ -0,0 +1,35 @@
1
+ import {
2
+ ConnectorApp,
3
+ Ingestor,
4
+ RuntimeConfig,
5
+ StateStore,
6
+ Telemetry,
7
+ } from "@useairfoil/connector-kit";
8
+ import { Effect, Layer, Logger } from "effect";
9
+ import { Command } from "effect/cli";
10
+
11
+ import { LinearConnector } from "./index";
12
+
13
+ const ConnectorLayer = LinearConnector.layerConfig(LinearConnector.LinearConfigDef.config);
14
+
15
+ const TelemetryLayer = Layer.mergeAll(Telemetry.layerOtlp(), Telemetry.layerMetricsConsoleDump());
16
+
17
+ export const sandboxCommand = Command.make("sandbox", {}, () =>
18
+ Effect.gen(function* () {
19
+ const port = yield* RuntimeConfig.httpPort;
20
+ const entrypoint = yield* LinearConnector.LinearConnector;
21
+
22
+ return yield* ConnectorApp.start(entrypoint, { port });
23
+ }).pipe(
24
+ Effect.annotateLogs({ component: "linear" }),
25
+ Effect.provide(
26
+ Layer.mergeAll(
27
+ StateStore.layerMemory,
28
+ Ingestor.layerConsole,
29
+ ConnectorLayer,
30
+ Logger.layer([Logger.consolePretty()]),
31
+ TelemetryLayer,
32
+ ),
33
+ ),
34
+ ),
35
+ ).pipe(Command.withDescription("Run the connector locally and log ingested data"));
@@ -0,0 +1,52 @@
1
+ import { Iceberg } from "@useairfoil/connector-kit";
2
+ import { Schema, Struct } from "effect";
3
+
4
+ import { OptionalRef, OptionalString, OptionalTimestamp, common, deleted } from "./shared";
5
+
6
+ export const CommentSchema = Schema.Struct({
7
+ ...common("Comment"),
8
+ _deleted: deleted("comment"),
9
+ body: Schema.String.pipe(Schema.annotateKey({ description: "Comment text in markdown." })),
10
+ user_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who wrote the comment." })),
11
+ external_user_id: OptionalRef.pipe(
12
+ Schema.annotateKey({ description: "External user who wrote the comment." }),
13
+ ),
14
+ issue_id: OptionalString.pipe(Schema.annotateKey({ description: "Issue the comment is on." })),
15
+ project_id: OptionalString.pipe(
16
+ Schema.annotateKey({ description: "Project the comment is on." }),
17
+ ),
18
+ project_update_id: OptionalString.pipe(
19
+ Schema.annotateKey({ description: "Project update the comment is on." }),
20
+ ),
21
+ initiative_id: OptionalString.pipe(
22
+ Schema.annotateKey({ description: "Initiative the comment is on." }),
23
+ ),
24
+ initiative_update_id: OptionalString.pipe(
25
+ Schema.annotateKey({ description: "Initiative update the comment is on." }),
26
+ ),
27
+ document_content_id: OptionalString.pipe(
28
+ Schema.annotateKey({ description: "Document content the comment is on." }),
29
+ ),
30
+ parent_id: OptionalString.pipe(
31
+ Schema.annotateKey({ description: "Comment this one replies to." }),
32
+ ),
33
+ quoted_text: OptionalString.pipe(Schema.annotateKey({ description: "Text the comment quotes." })),
34
+ edited_at: OptionalTimestamp.pipe(
35
+ Schema.annotateKey({ description: "Time the comment was last edited." }),
36
+ ),
37
+ resolved_at: OptionalTimestamp.pipe(
38
+ Schema.annotateKey({ description: "Time the thread was resolved." }),
39
+ ),
40
+ resolving_user_id: OptionalRef.pipe(
41
+ Schema.annotateKey({ description: "User who resolved the thread." }),
42
+ ),
43
+ url: Schema.String.pipe(Schema.annotateKey({ description: "Comment URL." })),
44
+ })
45
+ .annotate({ description: "Linear comments on issues, projects, initiatives, and updates." })
46
+ .pipe(Iceberg.identifier("id"));
47
+
48
+ export type Comment = Schema.Schema.Type<typeof CommentSchema>;
49
+
50
+ export const CommentObjectSchema = CommentSchema.mapFields((fields) =>
51
+ Struct.omit(fields, ["version", "_deleted"]),
52
+ );
@@ -0,0 +1,96 @@
1
+ import { Iceberg } from "@useairfoil/connector-kit";
2
+ import { Schema, Struct } from "effect";
3
+
4
+ import { Long, OptionalRef, OptionalString, Ref, common, deleted } from "./shared";
5
+
6
+ export const CustomerSchema = Schema.Struct({
7
+ ...common("Customer"),
8
+ _deleted: deleted("customer"),
9
+ name: Schema.String.pipe(Schema.annotateKey({ description: "Customer name." })),
10
+ domains: Schema.Array(Schema.String).pipe(
11
+ Schema.annotateKey({ description: "Email domains of the customer." }),
12
+ ),
13
+ external_ids: Schema.Array(Schema.String).pipe(
14
+ Schema.annotateKey({ description: "IDs in other systems." }),
15
+ ),
16
+ logo_url: OptionalString.pipe(Schema.annotateKey({ description: "Logo URL." })),
17
+ owner_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who owns the customer." })),
18
+ status_id: Ref.pipe(Schema.annotateKey({ description: "Customer status." })),
19
+ tier_id: OptionalRef.pipe(Schema.annotateKey({ description: "Customer tier." })),
20
+ revenue: Schema.NullOr(Long).pipe(Schema.annotateKey({ description: "Annual revenue." })),
21
+ size: Schema.NullOr(Schema.Finite).pipe(Schema.annotateKey({ description: "Company size." })),
22
+ approximate_need_count: Schema.Finite.pipe(
23
+ Schema.annotateKey({ description: "About how many requests it has." }),
24
+ ),
25
+ url: Schema.String.pipe(Schema.annotateKey({ description: "Customer URL." })),
26
+ })
27
+ .annotate({ description: "Linear customers." })
28
+ .pipe(Iceberg.identifier("id"));
29
+
30
+ export type Customer = Schema.Schema.Type<typeof CustomerSchema>;
31
+
32
+ export const CustomerObjectSchema = CustomerSchema.mapFields((fields) =>
33
+ Struct.omit(fields, ["version", "_deleted"]),
34
+ );
35
+
36
+ export const CustomerNeedSchema = Schema.Struct({
37
+ ...common("Request"),
38
+ _deleted: deleted("request"),
39
+ customer_id: OptionalRef.pipe(Schema.annotateKey({ description: "Customer." })),
40
+ issue_id: OptionalRef.pipe(
41
+ Schema.annotateKey({ description: "Issue the request is linked to." }),
42
+ ),
43
+ project_id: OptionalRef.pipe(
44
+ Schema.annotateKey({ description: "Project the request is linked to." }),
45
+ ),
46
+ original_issue_id: OptionalRef.pipe(
47
+ Schema.annotateKey({ description: "Issue the request was first linked to." }),
48
+ ),
49
+ comment_id: OptionalRef.pipe(
50
+ Schema.annotateKey({ description: "Comment the request came from." }),
51
+ ),
52
+ attachment_id: OptionalRef.pipe(
53
+ Schema.annotateKey({ description: "Attachment the request came from." }),
54
+ ),
55
+ creator_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who added the request." })),
56
+ body: OptionalString.pipe(Schema.annotateKey({ description: "Request text in markdown." })),
57
+ priority: Schema.Finite.pipe(Schema.annotateKey({ description: "0 normal, 1 important." })),
58
+ url: OptionalString.pipe(Schema.annotateKey({ description: "Source URL." })),
59
+ })
60
+ .annotate({ description: "Linear customer requests, linked to issues or projects." })
61
+ .pipe(Iceberg.identifier("id"));
62
+
63
+ export type CustomerNeed = Schema.Schema.Type<typeof CustomerNeedSchema>;
64
+
65
+ export const CustomerNeedObjectSchema = CustomerNeedSchema.mapFields((fields) =>
66
+ Struct.omit(fields, ["version", "_deleted"]),
67
+ );
68
+
69
+ const levelFields = (object: string) => ({
70
+ ...common(object),
71
+ name: Schema.String.pipe(Schema.annotateKey({ description: "Name." })),
72
+ display_name: Schema.String.pipe(Schema.annotateKey({ description: "Name shown in Linear." })),
73
+ description: OptionalString.pipe(Schema.annotateKey({ description: "Description." })),
74
+ color: Schema.String.pipe(Schema.annotateKey({ description: "Color, as hex." })),
75
+ position: Schema.Finite.pipe(Schema.annotateKey({ description: "Order in the list." })),
76
+ });
77
+
78
+ export const CustomerStatusSchema = Schema.Struct(levelFields("Status"))
79
+ .annotate({ description: "Linear customer statuses. Names the IDs in `customers.status_id`." })
80
+ .pipe(Iceberg.identifier("id"));
81
+
82
+ export type CustomerStatus = Schema.Schema.Type<typeof CustomerStatusSchema>;
83
+
84
+ export const CustomerStatusObjectSchema = CustomerStatusSchema.mapFields((fields) =>
85
+ Struct.omit(fields, ["version"]),
86
+ );
87
+
88
+ export const CustomerTierSchema = Schema.Struct(levelFields("Tier"))
89
+ .annotate({ description: "Linear customer tiers. Names the IDs in `customers.tier_id`." })
90
+ .pipe(Iceberg.identifier("id"));
91
+
92
+ export type CustomerTier = Schema.Schema.Type<typeof CustomerTierSchema>;
93
+
94
+ export const CustomerTierObjectSchema = CustomerTierSchema.mapFields((fields) =>
95
+ Struct.omit(fields, ["version"]),
96
+ );
@@ -0,0 +1,40 @@
1
+ import { Iceberg } from "@useairfoil/connector-kit";
2
+ import { Schema, Struct } from "effect";
3
+
4
+ import { OptionalRef, OptionalString, OptionalTimestamp, common, deleted } from "./shared";
5
+
6
+ export const DocumentSchema = Schema.Struct({
7
+ ...common("Document"),
8
+ _deleted: deleted("document"),
9
+ title: Schema.String.pipe(Schema.annotateKey({ description: "Title." })),
10
+ content: OptionalString.pipe(Schema.annotateKey({ description: "Content in markdown." })),
11
+ url: Schema.String.pipe(Schema.annotateKey({ description: "Document URL." })),
12
+ icon: OptionalString.pipe(Schema.annotateKey({ description: "Icon." })),
13
+ color: OptionalString.pipe(Schema.annotateKey({ description: "Icon color." })),
14
+ creator_id: OptionalRef.pipe(Schema.annotateKey({ description: "User who made the document." })),
15
+ updated_by_id: OptionalRef.pipe(
16
+ Schema.annotateKey({ description: "User who last changed the document." }),
17
+ ),
18
+ project_id: OptionalRef.pipe(Schema.annotateKey({ description: "Project the document is in." })),
19
+ initiative_id: OptionalRef.pipe(
20
+ Schema.annotateKey({ description: "Initiative the document is in." }),
21
+ ),
22
+ issue_id: OptionalRef.pipe(Schema.annotateKey({ description: "Issue the document is in." })),
23
+ cycle_id: OptionalRef.pipe(Schema.annotateKey({ description: "Cycle the document is in." })),
24
+ team_id: OptionalRef.pipe(Schema.annotateKey({ description: "Team the document is in." })),
25
+ parent_document_id: OptionalRef.pipe(Schema.annotateKey({ description: "Parent document." })),
26
+ hidden_at: OptionalTimestamp.pipe(
27
+ Schema.annotateKey({ description: "Time the document was hidden." }),
28
+ ),
29
+ trashed: Schema.NullOr(Schema.Boolean).pipe(
30
+ Schema.annotateKey({ description: "Whether the document is in the trash." }),
31
+ ),
32
+ })
33
+ .annotate({ description: "Linear documents, including archived and trashed ones." })
34
+ .pipe(Iceberg.identifier("id"));
35
+
36
+ export type Document = Schema.Schema.Type<typeof DocumentSchema>;
37
+
38
+ export const DocumentObjectSchema = DocumentSchema.mapFields((fields) =>
39
+ Struct.omit(fields, ["version", "_deleted"]),
40
+ );