clivly 0.9.3 → 0.9.5

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 (60) hide show
  1. package/README.md +23 -6
  2. package/core-entity-config.cjs +1 -1
  3. package/core-entity-config.mjs +1 -1
  4. package/core-mappings-config.cjs +1 -1
  5. package/core-mappings-config.mjs +1 -1
  6. package/core-sync-engine.cjs +1 -1
  7. package/core-sync-engine.mjs +1 -1
  8. package/core-view-compiler.cjs +1 -1
  9. package/core-view-compiler.mjs +1 -1
  10. package/core.cjs +11 -6
  11. package/core.d.cts +2 -2
  12. package/core.d.mts +2 -2
  13. package/core.mjs +8 -7
  14. package/drizzle.cjs +74 -9
  15. package/drizzle.d.cts +83 -7
  16. package/drizzle.d.mts +83 -7
  17. package/drizzle.mjs +74 -10
  18. package/{entity-config-DtBsl2MQ.cjs → entity-config-7sGCk_nw.cjs} +1 -1
  19. package/{entity-config-kUHri9yA.mjs → entity-config-B74SPOUZ.mjs} +1 -1
  20. package/index.cjs +180 -21
  21. package/index.mjs +180 -21
  22. package/{internal-Df-PtOH_.mjs → internal-CykXpEGk.mjs} +2 -2
  23. package/{internal-CwnEG6Wl.cjs → internal-DqduZU3l.cjs} +2 -2
  24. package/internal-entity-config.cjs +1 -1
  25. package/internal-entity-config.mjs +1 -1
  26. package/internal-entity-heuristics.cjs +1 -1
  27. package/internal-entity-heuristics.mjs +1 -1
  28. package/internal-mappings-config.cjs +1 -1
  29. package/internal-mappings-config.mjs +1 -1
  30. package/{internal-CDY8g9Ic.d.mts → internal-o2M7FGlR.d.mts} +29 -1
  31. package/internal-sync-engine.cjs +1 -1
  32. package/internal-sync-engine.mjs +1 -1
  33. package/internal-view-compiler.cjs +1 -1
  34. package/internal-view-compiler.mjs +1 -1
  35. package/{internal-Da_dlcQ_.d.cts → internal-wCXSELDN.d.cts} +29 -1
  36. package/internal.cjs +11 -6
  37. package/internal.d.cts +2 -2
  38. package/internal.d.mts +2 -2
  39. package/internal.mjs +8 -7
  40. package/{mappings-config-B0RqFLxu.cjs → mappings-config-CLyEg0sf.cjs} +1 -1
  41. package/{mappings-config-B1b1HVBg.mjs → mappings-config-DQS_s4dg.mjs} +1 -1
  42. package/{namespace-probe-EYyxWZnf.d.cts → namespace-probe-BoGI18dQ.d.cts} +8 -0
  43. package/{namespace-probe-BmdGnYMn.d.mts → namespace-probe-Cn7voAd0.d.mts} +8 -0
  44. package/package.json +1 -1
  45. package/{projection-D7xmQP4S.mjs → projection-CSGJbZlZ.mjs} +25 -4
  46. package/{projection-C5244vGa.cjs → projection-Dz6_i1-w.cjs} +25 -4
  47. package/projection-sql-BgnfiGqF.mjs +65 -0
  48. package/projection-sql-Cl1qIn8m.cjs +94 -0
  49. package/{sdk-COLUHhJq.cjs → sdk-CM1UO-I7.cjs} +58 -15
  50. package/{sdk-CZNXBsgA.mjs → sdk-DYBDR939.mjs} +53 -16
  51. package/sdk.cjs +3 -2
  52. package/sdk.d.cts +5 -2
  53. package/sdk.d.mts +5 -2
  54. package/sdk.mjs +3 -3
  55. package/{sync-engine-abE94g6X.cjs → sync-engine-C4A5jgA4.cjs} +1 -1
  56. package/{sync-engine-SuxSzbZ4.mjs → sync-engine-Dt5QQSKO.mjs} +1 -1
  57. package/{view-compiler-Blu4OXJ7.cjs → view-compiler-B5nDal14.cjs} +21 -74
  58. package/{view-compiler-CQx8TRrW.mjs → view-compiler-CSYwEWf5.mjs} +2 -55
  59. package/vite.d.cts +1 -1
  60. package/vite.d.mts +1 -1
package/README.md CHANGED
@@ -87,7 +87,10 @@ Detects your framework, ORM, auth adapter and app layout, then scaffolds:
87
87
  | The auth-verify route for your framework | Yes — `--force` to overwrite |
88
88
 
89
89
  Plus `CLIVLY_SECRET_KEY`, `CLIVLY_SYNC_TRIGGER_URL` and
90
- `CLIVLY_SYNC_TRIGGER_SECRET` placeholders in your env file.
90
+ `CLIVLY_SYNC_TRIGGER_SECRET` in your env file. The two secrets are written
91
+ empty, for you or `clivly login` to fill; the trigger URL gets a visible
92
+ `https://<yourDomain.com>/api/clivly/tick` template, because there the shape is
93
+ the documentation.
91
94
 
92
95
  | Flag | Purpose |
93
96
  | --- | --- |
@@ -233,7 +236,7 @@ the CLI requires pairing.
233
236
  | --- | --- | --- |
234
237
  | `CLIVLY_SECRET_KEY` | Dashboard → **Connect app**, or **Settings → API keys** | **Shown once.** Copy it immediately; you cannot retrieve it later, only regenerate. Regenerating **revokes the previous key**, so redeploy after rotating. The legacy name `CLIVLY_API_KEY` is still accepted as a fallback |
235
238
  | `CLIVLY_SYNC_TRIGGER_SECRET` | Dashboard → **Integrations → Remote sync** | Must match the value in your app's env exactly. A mismatch surfaces as a `401` on the signed trigger |
236
- | `CLIVLY_SYNC_TRIGGER_URL` | Your own deployment | The public URL of your mounted tick route, e.g. `https://app.example.com/api/clivly/tick`. Optional until you deploy — push-only setups can leave it unset |
239
+ | `CLIVLY_SYNC_TRIGGER_URL` | Your own deployment | The public URL of your mounted tick route. `init` and `login` both scaffold it as `https://<yourDomain.com>/api/clivly/tick` — the angle brackets mark it unedited, and the SDK ignores it until you replace them, so leaving it alone costs nothing. Optional until you deploy — push-only setups can leave it as the template |
237
240
  | `CLIVLY_PUBLISHABLE_KEY` | Same place as the secret key | Only needed for the chat widget. Safe to expose in client code |
238
241
 
239
242
  Write them into the env file of the package that imports `clivly`, then re-run
@@ -265,7 +268,7 @@ It answers two questions under two headings:
265
268
  scope; `clivly doctor` is local by definition and omits it.
266
269
  - **This project** — the local ladder, run here on your machine: credential →
267
270
  Clivly reachable → `clivly.config` loads → `clivly.config` is edge-safe →
268
- setup checks → sync trigger URL.
271
+ boot hook wired → setup checks → sync trigger URL.
269
272
 
270
273
  ```text
271
274
  Clivly status
@@ -278,11 +281,23 @@ Workspace
278
281
  🔒 Let Clivly start a sync
279
282
 
280
283
  This project
281
- ✅ Step 1 of 6 · API key present
282
- ✅ Step 2 of 6 · Clivly reachable — Acme
284
+ ✅ Step 1 of 7 · API key present
285
+ ✅ Step 2 of 7 · Clivly reachable — Acme
283
286
  ...
284
287
  ```
285
288
 
289
+ **Boot hook wired** is the rung to know about. It finds the boot site for your
290
+ framework and confirms something there actually starts Clivly — and on Next.js
291
+ below 15, that `experimental.instrumentationHook` is set in `next.config`,
292
+ without which `instrumentation.ts` is ignored outright and your app never checks
293
+ in. That failure is otherwise completely silent: the file is present, the config
294
+ loads, the credentials work, and the dashboard stays empty.
295
+
296
+ It is the one rung with no prerequisites, so it still reports when the config
297
+ above it fails to load. A framework the CLI does not recognise shows it as
298
+ skipped rather than passed — there is nowhere known to look, and a green step
299
+ for an unasked question is what made this worth adding.
300
+
286
301
  If the Workspace section reads
287
302
  `⚠️ This Clivly server does not report setup progress`, or names a rejected key
288
303
  or an unreachable host, that means the question could not be asked — not that
@@ -406,7 +421,7 @@ work, and are kept because output and docs have recommended them — but prefer
406
421
  | --- | --- |
407
422
  | `clivly ping` | Credential + reachability |
408
423
  | `clivly connect` | The above, plus config and trigger URL |
409
- | `clivly doctor` | Config, setup checks and edge safety |
424
+ | `clivly doctor` | Config, edge safety, boot hook and setup checks |
410
425
 
411
426
  Each prints a deprecation notice, then the steps it covers, renumbered to its own
412
427
  scope:
@@ -478,6 +493,8 @@ socket reconnect. The CLI does not enable this setting on your behalf.
478
493
  | `init` cannot find your schema | Pass `--schema <path\|package>` |
479
494
  | `init` scaffolded a `db` import that doesn't resolve | The probe cannot evaluate a db module that throws without `DATABASE_URL`. Fix the `TODO(clivly)` import path it left, or pass `--db-import` |
480
495
  | `clivly login` exits non-zero, pairing unavailable | The backend has `CLIVLY_CLI_PAIRING_ENABLED` off. Use the manual credential path above |
496
+ | App runs, no errors, dashboard stays silent | Run `clivly status` — the **Boot hook wired** step names the cause. On Next.js below 15 it is almost always the missing `experimental: { instrumentationHook: true }` in `next.config` |
497
+ | `Clivly reachable` fails with `invalid_api_key` (401) | The key is not one this org accepts. Re-run `npx clivly login` to re-pair, and check the key belongs to the org whose dashboard you are watching |
481
498
  | `clivly dev` cannot start | Install `cloudflared`, or set `CLIVLY_SYNC_TRIGGER_URL` to a tunnel you run yourself |
482
499
  | Credentials written but the app never loads them | Wrong env file for your framework — see the table under `clivly login` |
483
500
 
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_entity_config = require("./entity-config-DtBsl2MQ.cjs");
2
+ const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
3
3
  //#region src/core-entity-config.ts
4
4
  require("./deprecate-subpath-B9ngvizt.cjs").warnDeprecatedSubpath("clivly/core/entity-config", "clivly/internal/entity-config");
5
5
  //#endregion
@@ -1,4 +1,4 @@
1
- import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-kUHri9yA.mjs";
1
+ import { a as MATERIALIZING_CONCEPTS, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, i as ClivlyConfigError, l as filterSchema, n as CRM_CONCEPTS, o as customObjectDeclarations, p as validateEntitiesConfig, r as CUSTOM_CONCEPT, s as defineClivlyConfig, t as CANONICAL_FIELDS, u as projectionFields } from "./entity-config-B74SPOUZ.mjs";
2
2
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
3
3
  //#region src/core-entity-config.ts
4
4
  warnDeprecatedSubpath("clivly/core/entity-config", "clivly/internal/entity-config");
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_mappings_config = require("./mappings-config-B0RqFLxu.cjs");
2
+ const require_mappings_config = require("./mappings-config-CLyEg0sf.cjs");
3
3
  const require_deprecate_subpath = require("./deprecate-subpath-B9ngvizt.cjs");
4
4
  require("./internal-mappings-config.cjs");
5
5
  //#region src/core-mappings-config.ts
@@ -1,4 +1,4 @@
1
- import { n as mappingsToEntitiesConfig, t as entitiesToMappings } from "./mappings-config-B1b1HVBg.mjs";
1
+ import { n as mappingsToEntitiesConfig, t as entitiesToMappings } from "./mappings-config-DQS_s4dg.mjs";
2
2
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
3
3
  import "./internal-mappings-config.mjs";
4
4
  //#region src/core-mappings-config.ts
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_deprecate_subpath = require("./deprecate-subpath-B9ngvizt.cjs");
3
- const require_sync_engine = require("./sync-engine-abE94g6X.cjs");
3
+ const require_sync_engine = require("./sync-engine-C4A5jgA4.cjs");
4
4
  require("./internal-sync-engine.cjs");
5
5
  //#region src/core-sync-engine.ts
6
6
  require_deprecate_subpath.warnDeprecatedSubpath("clivly/core/sync-engine", "clivly/internal/sync-engine");
@@ -1,5 +1,5 @@
1
1
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
2
- import { i as runSync, n as previewSync, r as reconcile, t as computeSampleWarnings } from "./sync-engine-SuxSzbZ4.mjs";
2
+ import { i as runSync, n as previewSync, r as reconcile, t as computeSampleWarnings } from "./sync-engine-Dt5QQSKO.mjs";
3
3
  import "./internal-sync-engine.mjs";
4
4
  //#region src/core-sync-engine.ts
5
5
  warnDeprecatedSubpath("clivly/core/sync-engine", "clivly/internal/sync-engine");
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_deprecate_subpath = require("./deprecate-subpath-B9ngvizt.cjs");
3
- const require_view_compiler = require("./view-compiler-Blu4OXJ7.cjs");
3
+ const require_view_compiler = require("./view-compiler-B5nDal14.cjs");
4
4
  require("./internal-view-compiler.cjs");
5
5
  //#region src/core-view-compiler.ts
6
6
  require_deprecate_subpath.warnDeprecatedSubpath("clivly/core/view-compiler", "clivly/internal/view-compiler");
@@ -1,5 +1,5 @@
1
1
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
2
- import { a as explainViewExecutionError, i as explainEntitiesConfig, n as compileEntityView, r as compileEntityViews, t as DEFAULT_VIEW_PREFIX } from "./view-compiler-CQx8TRrW.mjs";
2
+ import { a as explainViewExecutionError, i as explainEntitiesConfig, n as compileEntityView, r as compileEntityViews, t as DEFAULT_VIEW_PREFIX } from "./view-compiler-CSYwEWf5.mjs";
3
3
  import "./internal-view-compiler.mjs";
4
4
  //#region src/core-view-compiler.ts
5
5
  warnDeprecatedSubpath("clivly/core/view-compiler", "clivly/internal/view-compiler");
package/core.cjs CHANGED
@@ -1,13 +1,14 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_projection = require("./projection-C5244vGa.cjs");
3
- const require_entity_config = require("./entity-config-DtBsl2MQ.cjs");
4
- const require_mappings_config = require("./mappings-config-B0RqFLxu.cjs");
2
+ const require_projection = require("./projection-Dz6_i1-w.cjs");
3
+ const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
4
+ const require_mappings_config = require("./mappings-config-CLyEg0sf.cjs");
5
5
  const require_deprecate_subpath = require("./deprecate-subpath-B9ngvizt.cjs");
6
6
  const require_auth_adapter = require("./auth-adapter-CiEXcweZ.cjs");
7
7
  const require_mapping_form = require("./mapping-form-BpfON_ME.cjs");
8
- const require_internal = require("./internal-CwnEG6Wl.cjs");
9
- const require_view_compiler = require("./view-compiler-Blu4OXJ7.cjs");
10
- const require_sync_engine = require("./sync-engine-abE94g6X.cjs");
8
+ const require_internal = require("./internal-DqduZU3l.cjs");
9
+ const require_projection_sql = require("./projection-sql-Cl1qIn8m.cjs");
10
+ const require_view_compiler = require("./view-compiler-B5nDal14.cjs");
11
+ const require_sync_engine = require("./sync-engine-C4A5jgA4.cjs");
11
12
  //#region src/core.ts
12
13
  require_deprecate_subpath.warnDeprecatedSubpath("clivly/core", "clivly/sdk", "Import \"defineClivlyConfig\" and its config types from \"clivly/sdk\"; import everything else from \"clivly/internal\" (no stability guarantee — see STABILITY.md).");
13
14
  //#endregion
@@ -23,6 +24,9 @@ exports.buildRelationships = require_mapping_form.buildRelationships;
23
24
  exports.columnMetaFor = require_entity_config.columnMetaFor;
24
25
  exports.compileEntityView = require_view_compiler.compileEntityView;
25
26
  exports.compileEntityViews = require_view_compiler.compileEntityViews;
27
+ exports.compileProjectionFrom = require_projection_sql.compileProjectionFrom;
28
+ exports.compileProjectionIdentity = require_projection_sql.compileProjectionIdentity;
29
+ exports.compileProjectionJoins = require_projection_sql.compileProjectionJoins;
26
30
  exports.coversUniqueKey = require_entity_config.coversUniqueKey;
27
31
  exports.defineClivlyConfig = require_entity_config.defineClivlyConfig;
28
32
  exports.describeCardinality = require_internal.describeCardinality;
@@ -39,6 +43,7 @@ exports.joinFansOut = require_entity_config.joinFansOut;
39
43
  exports.mappingsToEntitiesConfig = require_mappings_config.mappingsToEntitiesConfig;
40
44
  exports.primaryKeyColumns = require_entity_config.primaryKeyColumns;
41
45
  exports.projectionSchema = require_projection.projectionSchema;
46
+ exports.quoteIdent = require_projection_sql.quoteIdent;
42
47
  exports.reconcile = require_sync_engine.reconcile;
43
48
  exports.relationshipSchema = require_entity_config.relationshipSchema;
44
49
  exports.relationshipViaSignature = require_mapping_form.relationshipViaSignature;
package/core.d.cts CHANGED
@@ -7,5 +7,5 @@ import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-CmEdAMwj.cjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-BG2O1an2.cjs";
9
9
  import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.cjs";
10
- import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-Da_dlcQ_.cjs";
11
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
10
+ import { _ as joinFansOut, a as validateProjection, c as compileProjectionJoins, d as describeCardinality, f as draftErrors, g as fanOutJoins, h as suggestJoinConditions, i as primaryKeyColumns, l as quoteIdent, m as emptyProjectionDraft, n as coversUniqueKey, o as compileProjectionFrom, p as draftToProjection, r as hasRichMeta, s as compileProjectionIdentity, t as columnMetaFor, u as ProjectionDraft } from "./internal-wCXSELDN.cjs";
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/core.d.mts CHANGED
@@ -7,5 +7,5 @@ import { i as mappingsToEntitiesConfig, t as EntityMappingRow } from "./mappings
7
7
  import { a as compileEntityView, i as ExplainResult, n as CompiledView, o as compileEntityViews, r as DEFAULT_VIEW_PREFIX, s as explainEntitiesConfig, t as CompileOptions } from "./view-compiler-Bo7pqXW3.mjs";
8
8
  import { c as SyncResult, f as reconcile, l as SyncStore, n as MirrorRecord, o as SourceRow, p as runSync, r as PersistAction, s as SyncAction, t as EntitySyncResult } from "./sync-engine-DgQbrJON.mjs";
9
9
  import { a as IntegrationStep, c as StepProblem, i as IntegrationStatus, l as StepState, n as IntegrationFacts, o as NextAction, r as IntegrationState, s as StepId, t as ConnectionLiveness } from "./integration-state-BK-riZnm.mjs";
10
- import { a as validateProjection, c as draftErrors, d as suggestJoinConditions, f as fanOutJoins, i as primaryKeyColumns, l as draftToProjection, n as coversUniqueKey, o as ProjectionDraft, p as joinFansOut, r as hasRichMeta, s as describeCardinality, t as columnMetaFor, u as emptyProjectionDraft } from "./internal-CDY8g9Ic.mjs";
11
- export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
10
+ import { _ as joinFansOut, a as validateProjection, c as compileProjectionJoins, d as describeCardinality, f as draftErrors, g as fanOutJoins, h as suggestJoinConditions, i as primaryKeyColumns, l as quoteIdent, m as emptyProjectionDraft, n as coversUniqueKey, o as compileProjectionFrom, p as draftToProjection, r as hasRichMeta, s as compileProjectionIdentity, t as columnMetaFor, u as ProjectionDraft } from "./internal-o2M7FGlR.mjs";
11
+ export { type Activity, type ActivityType, BASE_REF, CANONICAL_FIELDS, type CRMAdapter, CRM_CONCEPTS, type ClivlyAuthAdapter, ClivlyConfigError, type ClivlyEntitiesConfig, type ClivlyEntityConfig, type ClivlyUser, type Company, type CompileOptions, type CompiledView, type ConfigValidationError, type ConfigValidationResult, type ConnectionLiveness, type Contact, type ContactFilters, type ContactStatus, type CreateActivityInput, type CreateCompanyInput, type CreateContactInput, type CreateDealInput, type CreateTaskInput, type CrmConcept, type CrmMembership, type CrmRole, DEFAULT_VIEW_PREFIX, type Deal, type DealStage, type DiscoveredColumnMeta, type DiscoveredSchemaTable, type EntityMappingRow, type EntitySyncResult, type ExplainResult, type FilterExpr, type FilterValue, type IntegrationFacts, type IntegrationState, type IntegrationStatus, type IntegrationStep, MATERIALIZING_CONCEPTS, type MaterializingConcept, type MembershipResolver, type MirrorRecord, type NextAction, type Note, type PersistAction, type ProjectionDraft, type ProjectionFieldBinding, type ProjectionIdentity, type ProjectionJoin, type ProjectionMapping, type ProjectionRef, type RecordSource, type RelationshipCandidate, type RelationshipSpec, type RelationshipVia, type RelationshipsMap, type SourceRow, type StepId, type StepProblem, type StepState, type SyncAction, type SyncResult, type SyncStore, type Task, type TaskEntityType, type TaskFilters, type TaskPriority, type TaskStatus, type UpdateContactInput, type UpdateDealInput, type UpdateTaskInput, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/core.mjs CHANGED
@@ -1,13 +1,14 @@
1
- import { i as projectionSchema, r as identityRefs, t as BASE_REF } from "./projection-D7xmQP4S.mjs";
2
- import { _ as columnMetaFor, a as MATERIALIZING_CONCEPTS, b as primaryKeyColumns, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, g as joinFansOut, h as fanOutJoins, i as ClivlyConfigError, l as filterSchema, m as validateProjection, n as CRM_CONCEPTS, p as validateEntitiesConfig, s as defineClivlyConfig, t as CANONICAL_FIELDS, v as coversUniqueKey, y as hasRichMeta } from "./entity-config-kUHri9yA.mjs";
3
- import { n as mappingsToEntitiesConfig } from "./mappings-config-B1b1HVBg.mjs";
1
+ import { i as projectionSchema, r as identityRefs, t as BASE_REF } from "./projection-CSGJbZlZ.mjs";
2
+ import { _ as columnMetaFor, a as MATERIALIZING_CONCEPTS, b as primaryKeyColumns, c as entitiesConfigSchema, d as relationshipSchema, f as relationshipsSchema, g as joinFansOut, h as fanOutJoins, i as ClivlyConfigError, l as filterSchema, m as validateProjection, n as CRM_CONCEPTS, p as validateEntitiesConfig, s as defineClivlyConfig, t as CANONICAL_FIELDS, v as coversUniqueKey, y as hasRichMeta } from "./entity-config-B74SPOUZ.mjs";
3
+ import { n as mappingsToEntitiesConfig } from "./mappings-config-DQS_s4dg.mjs";
4
4
  import { t as warnDeprecatedSubpath } from "./deprecate-subpath-CmvZtCpE.mjs";
5
5
  import { t as authCustom } from "./auth-adapter-gfVDxf3d.mjs";
6
6
  import { n as buildRelationships, r as relationshipViaSignature, t as buildFilterFromDiscriminator } from "./mapping-form-DigkuvFc.mjs";
7
- import { a as suggestJoinConditions, i as emptyProjectionDraft, n as draftErrors, r as draftToProjection, t as describeCardinality } from "./internal-Df-PtOH_.mjs";
8
- import { i as explainEntitiesConfig, n as compileEntityView, r as compileEntityViews, t as DEFAULT_VIEW_PREFIX } from "./view-compiler-CQx8TRrW.mjs";
9
- import { i as runSync, r as reconcile } from "./sync-engine-SuxSzbZ4.mjs";
7
+ import { a as suggestJoinConditions, i as emptyProjectionDraft, n as draftErrors, r as draftToProjection, t as describeCardinality } from "./internal-CykXpEGk.mjs";
8
+ import { a as quoteIdent, n as compileProjectionIdentity, r as compileProjectionJoins, t as compileProjectionFrom } from "./projection-sql-BgnfiGqF.mjs";
9
+ import { i as explainEntitiesConfig, n as compileEntityView, r as compileEntityViews, t as DEFAULT_VIEW_PREFIX } from "./view-compiler-CSYwEWf5.mjs";
10
+ import { i as runSync, r as reconcile } from "./sync-engine-Dt5QQSKO.mjs";
10
11
  //#region src/core.ts
11
12
  warnDeprecatedSubpath("clivly/core", "clivly/sdk", "Import \"defineClivlyConfig\" and its config types from \"clivly/sdk\"; import everything else from \"clivly/internal\" (no stability guarantee — see STABILITY.md).");
12
13
  //#endregion
13
- export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
14
+ export { BASE_REF, CANONICAL_FIELDS, CRM_CONCEPTS, ClivlyConfigError, DEFAULT_VIEW_PREFIX, MATERIALIZING_CONCEPTS, authCustom, buildFilterFromDiscriminator, buildRelationships, columnMetaFor, compileEntityView, compileEntityViews, compileProjectionFrom, compileProjectionIdentity, compileProjectionJoins, coversUniqueKey, defineClivlyConfig, describeCardinality, draftErrors, draftToProjection, emptyProjectionDraft, entitiesConfigSchema, explainEntitiesConfig, fanOutJoins, filterSchema, hasRichMeta, identityRefs, joinFansOut, mappingsToEntitiesConfig, primaryKeyColumns, projectionSchema, quoteIdent, reconcile, relationshipSchema, relationshipViaSignature, relationshipsSchema, runSync, suggestJoinConditions, validateEntitiesConfig, validateProjection };
package/drizzle.cjs CHANGED
@@ -1,7 +1,78 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_projection = require("./projection-Dz6_i1-w.cjs");
2
3
  const require_drizzle_meta = require("./drizzle-meta-Bb1JWYqf.cjs");
4
+ const require_projection_sql = require("./projection-sql-Cl1qIn8m.cjs");
3
5
  const require_drizzle = require("./drizzle-bfjVzYR6.cjs");
4
6
  let drizzle_orm = require("drizzle-orm");
7
+ //#region ../sdk/src/drizzle-projection.ts
8
+ /** `<ref>.<column>` — the token `fields` carries and cloud-ingest reads. */
9
+ function refToken(ref) {
10
+ return `${ref.table}.${ref.column}`;
11
+ }
12
+ function refSql(ref) {
13
+ return `${require_projection_sql.quoteIdent(ref.table)}.${require_projection_sql.quoteIdent(ref.column)}`;
14
+ }
15
+ function parseRef(value) {
16
+ if (typeof value !== "string") return value;
17
+ const dot = value.indexOf(".");
18
+ if (dot <= 0 || dot === value.length - 1) throw new Error(`fromDrizzleProjection: cursorField "${value}" must be a ref token like "base.updated_at".`);
19
+ return {
20
+ table: value.slice(0, dot),
21
+ column: value.slice(dot + 1)
22
+ };
23
+ }
24
+ /**
25
+ * Refs address tables symbolically — `base` or a join key — never by table name,
26
+ * which is what stops a projection reaching a table it did not declare. An
27
+ * unresolvable ref would otherwise compile to SQL referencing a missing alias
28
+ * and fail at the database with a message that never mentions the config.
29
+ */
30
+ function assertKnownRef(ref, known, context) {
31
+ if (!known.has(ref.table)) throw new Error(`fromDrizzleProjection: ${context} names unknown ref "${ref.table}" — expected "${[...known].join("\", \"")}".`);
32
+ }
33
+ function normalizeRows(result) {
34
+ if (Array.isArray(result)) return result;
35
+ const rows = result.rows;
36
+ if (Array.isArray(rows)) return rows;
37
+ throw new Error("fromDrizzleProjection: db.execute() returned neither an array of rows nor { rows }.");
38
+ }
39
+ function fromDrizzleProjection(db, projection, opts) {
40
+ const client = db;
41
+ const known = /* @__PURE__ */ new Set([require_projection.BASE_REF, ...projection.joins.map((j) => j.key)]);
42
+ if (projection.identity.kind === "base_pk" && projection.identity.column === void 0 && opts.basePkColumn === void 0) throw new Error(`fromDrizzleProjection: projection "${projection.entityKey}" has a base_pk identity with no column — pass { basePkColumn }.`);
43
+ const cursorRef = parseRef(opts.cursorField);
44
+ assertKnownRef(cursorRef, known, "cursorField");
45
+ for (const [field, binding] of Object.entries(projection.fieldBindings)) assertKnownRef(binding.from, known, `field "${field}"`);
46
+ const identityExpr = require_projection_sql.compileProjectionIdentity(projection, opts.basePkColumn ?? "");
47
+ const cursorToken = refToken(cursorRef);
48
+ const cursorExpr = `CAST(${refSql(cursorRef)} AS timestamptz)`;
49
+ const cursorRead = `to_char(${cursorExpr} AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`;
50
+ const selectItems = [`${identityExpr} AS ${require_projection_sql.quoteIdent("id")}`, `${cursorRead} AS ${require_projection_sql.quoteIdent(cursorToken)}`];
51
+ const emitted = /* @__PURE__ */ new Set([cursorToken]);
52
+ for (const binding of Object.values(projection.fieldBindings)) {
53
+ const token = refToken(binding.from);
54
+ if (emitted.has(token)) continue;
55
+ emitted.add(token);
56
+ selectItems.push(`${refSql(binding.from)} AS ${require_projection_sql.quoteIdent(token)}`);
57
+ }
58
+ const head = [
59
+ `select ${selectItems.join(", ")}`,
60
+ require_projection_sql.compileProjectionFrom(projection),
61
+ ...require_projection_sql.compileProjectionJoins(projection)
62
+ ].join(" ");
63
+ return {
64
+ entity: opts.entity,
65
+ cursorField: cursorToken,
66
+ idField: "id",
67
+ ...opts.objectType === void 0 ? {} : { objectType: opts.objectType },
68
+ fetchPage: async ({ since, limit }) => {
69
+ const where = since ? drizzle_orm.sql` where (${drizzle_orm.sql.raw(cursorExpr)}, ${drizzle_orm.sql.raw(identityExpr)}) > (${since.time.toISOString()}::timestamptz, ${since.id})` : drizzle_orm.sql``;
70
+ const query = drizzle_orm.sql`${drizzle_orm.sql.raw(head)}${where} order by ${drizzle_orm.sql.raw(cursorExpr)} asc, ${drizzle_orm.sql.raw(identityExpr)} asc limit ${limit}`;
71
+ return normalizeRows(await client.execute(query));
72
+ }
73
+ };
74
+ }
75
+ //#endregion
5
76
  //#region ../sdk/src/drizzle.ts
6
77
  function inferIdField(table) {
7
78
  const columns = (0, drizzle_orm.getTableColumns)(table);
@@ -14,14 +85,6 @@ function inferIdField(table) {
14
85
  const kind = primaryKeys.length === 0 ? "no" : "a composite";
15
86
  throw new Error(`fromDrizzle: table "${(0, drizzle_orm.getTableName)(table)}" has ${kind} single-column primary key — pass { idField } explicitly.`);
16
87
  }
17
- /**
18
- * Build a `SyncSource` from a Drizzle table, generating the keyset-paginated
19
- * `fetchPage` the SDK needs. `cursorField`/`idField` are drizzle property keys;
20
- * `idField` defaults to the table's single-column primary key.
21
- *
22
- * `drizzle-orm` is an optional peer dependency — installed only when you import
23
- * `@clivly/sdk/drizzle`.
24
- */
25
88
  function fromDrizzle(db, table, opts) {
26
89
  const drizzleDb = db;
27
90
  const idField = opts.idField ?? inferIdField(table);
@@ -38,7 +101,8 @@ function fromDrizzle(db, table, opts) {
38
101
  entity: opts.entity,
39
102
  cursorField: cursorColumnName,
40
103
  idField: idColumnName,
41
- fetchPage: ({ since, limit }) => drizzleDb.select(projection).from(table).where(since ? drizzle_orm.sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy((0, drizzle_orm.asc)(cursorColumn), (0, drizzle_orm.asc)(idColumn)).limit(limit)
104
+ fetchPage: ({ since, limit }) => drizzleDb.select(projection).from(table).where(since ? drizzle_orm.sql`(${cursorColumn}, ${idColumn}) > (${since.time}, ${since.id})` : void 0).orderBy((0, drizzle_orm.asc)(cursorColumn), (0, drizzle_orm.asc)(idColumn)).limit(limit),
105
+ ...opts.objectType === void 0 ? {} : { objectType: opts.objectType }
42
106
  };
43
107
  const uniqueConstraints = require_drizzle.buildUniqueConstraints(table);
44
108
  const meta = {
@@ -79,3 +143,4 @@ exports.buildUniqueConstraints = require_drizzle.buildUniqueConstraints;
79
143
  exports.discoverFromDrizzle = require_drizzle.discoverFromDrizzle;
80
144
  exports.drizzleIntrospector = drizzleIntrospector;
81
145
  exports.fromDrizzle = fromDrizzle;
146
+ exports.fromDrizzleProjection = fromDrizzleProjection;
package/drizzle.d.cts CHANGED
@@ -1,23 +1,99 @@
1
+ import { F as ProjectionRef, P as ProjectionMapping } from "./entity-config-B2RA53nw.cjs";
1
2
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-DEtRrqtc.cjs";
2
- import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-EYyxWZnf.cjs";
3
+ import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-BoGI18dQ.cjs";
3
4
  import { Table } from "drizzle-orm";
5
+ //#region ../sdk/src/drizzle-projection.d.ts
6
+ /**
7
+ * Build a `SyncSource` that actually executes a projection's joins.
8
+ *
9
+ * `fromDrizzle` emits a single-table select, so a projection entity paired with
10
+ * it pushed raw base-table rows and every joined field mirrored empty — the
11
+ * declared joins were inert, and the only symptom was a dry run reporting a
12
+ * missing column whose name was correct.
13
+ *
14
+ * The row shape is the contract the cloud already relies on. A projection's
15
+ * `fields` are ref *tokens* (`client_name -> "cl.full_name"`, from
16
+ * `projectionFields`), and `cloud-ingest` reads `row[token]` verbatim plus
17
+ * `row.id` for `sourceRef`. So this aliases every binding by its token rather
18
+ * than by its field name.
19
+ *
20
+ * That is the one deliberate difference from the view compiler, which aliases
21
+ * by field name because the sync engine reads the compiled view directly. FROM,
22
+ * JOIN and the identity expression all come from `@clivly/core`, so both paths
23
+ * join identically and produce the same `id` for the same row.
24
+ *
25
+ * `drizzle-orm` is an optional peer dependency — installed only when you import
26
+ * `@clivly/sdk/drizzle`. The `db` only needs `.execute()`, the same surface
27
+ * `drizzleIntrospector` requires.
28
+ */
29
+ interface FromDrizzleProjectionOptions {
30
+ /**
31
+ * The base table's primary key, required only when `identity` is `base_pk`
32
+ * without an explicit column. Unlike `fromDrizzle` there is no `Table` object
33
+ * to infer it from — a projection names tables as strings.
34
+ */
35
+ basePkColumn?: string;
36
+ /**
37
+ * The column to keyset-paginate on, as a ref token (`"base.updated_at"`) or
38
+ * a `ProjectionRef`. A projection spans several tables, so there is no
39
+ * single table to infer a cursor from.
40
+ */
41
+ cursorField: string | ProjectionRef;
42
+ /** The source name rows are pushed under. */
43
+ entity: string;
44
+ /**
45
+ * The custom object slug. Accepted here so the result is directly assignable
46
+ * to `source.custom` — bolting it on with a spread afterwards drops the
47
+ * non-enumerable drizzle metadata.
48
+ */
49
+ objectType?: string;
50
+ }
51
+ declare function fromDrizzleProjection(db: unknown, projection: ProjectionMapping, opts: FromDrizzleProjectionOptions & {
52
+ objectType: string;
53
+ }): SyncSource & {
54
+ objectType: string;
55
+ };
56
+ declare function fromDrizzleProjection(db: unknown, projection: ProjectionMapping, opts: FromDrizzleProjectionOptions): SyncSource;
57
+ //#endregion
4
58
  //#region ../sdk/src/drizzle.d.ts
5
59
  interface DrizzleDb {
6
60
  select: (...args: any[]) => any;
7
61
  }
62
+ interface FromDrizzleOptions {
63
+ cursorField: string;
64
+ entity: string;
65
+ idField?: string;
66
+ /**
67
+ * Custom-object slug, for sources destined for `source.custom`.
68
+ *
69
+ * Pass it here rather than attaching it afterwards. `source.custom` is typed
70
+ * `Array<SyncSource & { objectType: string }>`, so before this option existed
71
+ * the only way to satisfy that type was to bolt the property on post-hoc —
72
+ * and the idiomatic spread (`{ ...fromDrizzle(...), objectType }`) copies only
73
+ * ENUMERABLE own properties, silently dropping the non-enumerable
74
+ * `DRIZZLE_META` below. `doctor` would then report a `fromDrizzle` source as
75
+ * "hand-written" and skip its cursor/id column checks, which is both a lost
76
+ * check and a false explanation.
77
+ */
78
+ objectType?: string;
79
+ }
8
80
  /**
9
81
  * Build a `SyncSource` from a Drizzle table, generating the keyset-paginated
10
82
  * `fetchPage` the SDK needs. `cursorField`/`idField` are drizzle property keys;
11
83
  * `idField` defaults to the table's single-column primary key.
12
84
  *
85
+ * Pass `objectType` for custom objects — the result is then directly assignable
86
+ * to `source.custom` with no spread or mutation.
87
+ *
13
88
  * `drizzle-orm` is an optional peer dependency — installed only when you import
14
89
  * `@clivly/sdk/drizzle`.
15
90
  */
16
- declare function fromDrizzle(db: DrizzleDb, table: Table, opts: {
17
- entity: string;
18
- cursorField: string;
19
- idField?: string;
20
- }): SyncSource;
91
+ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: FromDrizzleOptions & {
92
+ objectType: string;
93
+ }): SyncSource & {
94
+ objectType: string;
95
+ };
96
+ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: FromDrizzleOptions): SyncSource;
21
97
  /**
22
98
  * Build a `NamespaceIntrospector` from any Drizzle client that exposes
23
99
  * `.execute()`. Reads `information_schema` in the connection's default schema
@@ -25,4 +101,4 @@ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: {
25
101
  */
26
102
  declare function drizzleIntrospector(db: unknown): NamespaceIntrospector;
27
103
  //#endregion
28
- export { buildUniqueConstraints, discoverFromDrizzle, drizzleIntrospector, fromDrizzle };
104
+ export { type FromDrizzleProjectionOptions, buildUniqueConstraints, discoverFromDrizzle, drizzleIntrospector, fromDrizzle, fromDrizzleProjection };
package/drizzle.d.mts CHANGED
@@ -1,23 +1,99 @@
1
+ import { F as ProjectionRef, P as ProjectionMapping } from "./entity-config-B2RA53nw.mjs";
1
2
  import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-CiK8MMEZ.mjs";
2
- import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-BmdGnYMn.mjs";
3
+ import { _ as SyncSource, t as NamespaceIntrospector } from "./namespace-probe-Cn7voAd0.mjs";
3
4
  import { Table } from "drizzle-orm";
5
+ //#region ../sdk/src/drizzle-projection.d.ts
6
+ /**
7
+ * Build a `SyncSource` that actually executes a projection's joins.
8
+ *
9
+ * `fromDrizzle` emits a single-table select, so a projection entity paired with
10
+ * it pushed raw base-table rows and every joined field mirrored empty — the
11
+ * declared joins were inert, and the only symptom was a dry run reporting a
12
+ * missing column whose name was correct.
13
+ *
14
+ * The row shape is the contract the cloud already relies on. A projection's
15
+ * `fields` are ref *tokens* (`client_name -> "cl.full_name"`, from
16
+ * `projectionFields`), and `cloud-ingest` reads `row[token]` verbatim plus
17
+ * `row.id` for `sourceRef`. So this aliases every binding by its token rather
18
+ * than by its field name.
19
+ *
20
+ * That is the one deliberate difference from the view compiler, which aliases
21
+ * by field name because the sync engine reads the compiled view directly. FROM,
22
+ * JOIN and the identity expression all come from `@clivly/core`, so both paths
23
+ * join identically and produce the same `id` for the same row.
24
+ *
25
+ * `drizzle-orm` is an optional peer dependency — installed only when you import
26
+ * `@clivly/sdk/drizzle`. The `db` only needs `.execute()`, the same surface
27
+ * `drizzleIntrospector` requires.
28
+ */
29
+ interface FromDrizzleProjectionOptions {
30
+ /**
31
+ * The base table's primary key, required only when `identity` is `base_pk`
32
+ * without an explicit column. Unlike `fromDrizzle` there is no `Table` object
33
+ * to infer it from — a projection names tables as strings.
34
+ */
35
+ basePkColumn?: string;
36
+ /**
37
+ * The column to keyset-paginate on, as a ref token (`"base.updated_at"`) or
38
+ * a `ProjectionRef`. A projection spans several tables, so there is no
39
+ * single table to infer a cursor from.
40
+ */
41
+ cursorField: string | ProjectionRef;
42
+ /** The source name rows are pushed under. */
43
+ entity: string;
44
+ /**
45
+ * The custom object slug. Accepted here so the result is directly assignable
46
+ * to `source.custom` — bolting it on with a spread afterwards drops the
47
+ * non-enumerable drizzle metadata.
48
+ */
49
+ objectType?: string;
50
+ }
51
+ declare function fromDrizzleProjection(db: unknown, projection: ProjectionMapping, opts: FromDrizzleProjectionOptions & {
52
+ objectType: string;
53
+ }): SyncSource & {
54
+ objectType: string;
55
+ };
56
+ declare function fromDrizzleProjection(db: unknown, projection: ProjectionMapping, opts: FromDrizzleProjectionOptions): SyncSource;
57
+ //#endregion
4
58
  //#region ../sdk/src/drizzle.d.ts
5
59
  interface DrizzleDb {
6
60
  select: (...args: any[]) => any;
7
61
  }
62
+ interface FromDrizzleOptions {
63
+ cursorField: string;
64
+ entity: string;
65
+ idField?: string;
66
+ /**
67
+ * Custom-object slug, for sources destined for `source.custom`.
68
+ *
69
+ * Pass it here rather than attaching it afterwards. `source.custom` is typed
70
+ * `Array<SyncSource & { objectType: string }>`, so before this option existed
71
+ * the only way to satisfy that type was to bolt the property on post-hoc —
72
+ * and the idiomatic spread (`{ ...fromDrizzle(...), objectType }`) copies only
73
+ * ENUMERABLE own properties, silently dropping the non-enumerable
74
+ * `DRIZZLE_META` below. `doctor` would then report a `fromDrizzle` source as
75
+ * "hand-written" and skip its cursor/id column checks, which is both a lost
76
+ * check and a false explanation.
77
+ */
78
+ objectType?: string;
79
+ }
8
80
  /**
9
81
  * Build a `SyncSource` from a Drizzle table, generating the keyset-paginated
10
82
  * `fetchPage` the SDK needs. `cursorField`/`idField` are drizzle property keys;
11
83
  * `idField` defaults to the table's single-column primary key.
12
84
  *
85
+ * Pass `objectType` for custom objects — the result is then directly assignable
86
+ * to `source.custom` with no spread or mutation.
87
+ *
13
88
  * `drizzle-orm` is an optional peer dependency — installed only when you import
14
89
  * `@clivly/sdk/drizzle`.
15
90
  */
16
- declare function fromDrizzle(db: DrizzleDb, table: Table, opts: {
17
- entity: string;
18
- cursorField: string;
19
- idField?: string;
20
- }): SyncSource;
91
+ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: FromDrizzleOptions & {
92
+ objectType: string;
93
+ }): SyncSource & {
94
+ objectType: string;
95
+ };
96
+ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: FromDrizzleOptions): SyncSource;
21
97
  /**
22
98
  * Build a `NamespaceIntrospector` from any Drizzle client that exposes
23
99
  * `.execute()`. Reads `information_schema` in the connection's default schema
@@ -25,4 +101,4 @@ declare function fromDrizzle(db: DrizzleDb, table: Table, opts: {
25
101
  */
26
102
  declare function drizzleIntrospector(db: unknown): NamespaceIntrospector;
27
103
  //#endregion
28
- export { buildUniqueConstraints, discoverFromDrizzle, drizzleIntrospector, fromDrizzle };
104
+ export { type FromDrizzleProjectionOptions, buildUniqueConstraints, discoverFromDrizzle, drizzleIntrospector, fromDrizzle, fromDrizzleProjection };