kalup 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/pull.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Pull
2
2
 
3
- `kalup pull [--target <name>]` reads one target and merges it into `hubspot/objects/*.ts`, `hubspot/pipelines/*.ts` and the target's `definition` overrides. It never writes to the portal (`E_WRITE_IN_READ_MODE`), records in state what the files and the portal agree on (below), and sanitizes portal strings it prints.
3
+ `kalup pull [--target <name>]` reads one target and merges it into `hubspot/objects/*.ts`, `hubspot/pipelines/*.ts`, `hubspot/associations.ts` and the target's `definition` overrides. It never writes to the portal (`E_WRITE_IN_READ_MODE`), records in state what the files and the portal agree on (below), and sanitizes portal strings it prints.
4
4
 
5
5
  This page is the reference. For the walk-through with examples, see [kalup pull](https://kalup.dev/docs/commands/pull) on the website.
6
6
 
@@ -8,7 +8,7 @@ This page is the reference. For the walk-through with examples, see [kalup pull]
8
8
 
9
9
  1. Validate, then pick the target (targets.md).
10
10
  2. The read key, then the portal guard (targets.md).
11
- 3. The read: custom object schemas when `objects` names one (or with `--discover`), then each object's properties (sensitive ones too) and groups, and its pipelines when they are in scope (below). A 403 on a list is `E_SCOPE`: that object is skipped and pull ends with `E_INCOMPLETE`. A 403 on the pipelines list skips only the object's pipelines.
11
+ 3. The read: custom object schemas when `objects` names one (or with `--discover`), then each object's properties (sensitive ones too) and groups, and its pipelines when they are in scope (below). A 403 on a list is `E_SCOPE`: that object is skipped and pull ends with `E_INCOMPLETE`. A 403 on the pipelines list skips only the object's pipelines. Then the labels of each object pair in association scope, both ways, and the schema read of each object of such a pair, for the internal names; a 403 on a labels list skips only that pair.
12
12
  4. Normalize. A `hubspotDefined` property, or one HubSpot calculates with a field type other than `calculation_equation`, becomes a reference; a custom `calculation_equation` property is managed with its `calculationFormula`. An owner property (select or radio) is `p.owner`, a `phone_number` one `p.phoneNumber`, rich text a `p.string` with `fieldType: 'html'`. A property Kalup does not write becomes a `p.string` reference, with `W_UNSUPPORTED_TYPE`: a `type` or custom `fieldType` no builder carries (`object_coordinates`, a rollup), or a custom `externalOptions` property that is no owner select or radio. A display field is kept only on the types that show it, and a field holding HubSpot's default is left out. Options are ordered by `displayOrder`, missing or negative last.
13
13
  5. Merge, with state where it owns a resource (below), then validate: an issue is `E_PULL_INVALID`, even with `--check`, and nothing is written.
14
14
  6. Write the changed files and `hubspot/index.ts` as one, each first copied to `.kalup/history/<timestamp>/`; a failure puts all back (`E_PROJECT_WRITE`).
@@ -22,6 +22,7 @@ This page is the reference. For the walk-through with examples, see [kalup pull]
22
22
  - `exclude: [...]`: internal names left out, `*` matching any run: never pulled, never archived by takeover. `include` wins over a pattern; a name in both is `E_SETTING_VALUE`, so `--discover` and the plan's notes say to take a listed name out of `exclude` rather than add it to `include`.
23
23
  - `as`: the export name for the first pull, by default PascalCase singular (`line_items` to `LineItem`).
24
24
  - `pipelines` (default `false`): every pipeline of the object. Without it, pull refreshes only the pipelines `hubspot/pipelines/<object>.ts` defines, and `--discover` lists the rest. `kalup init` sets it for deals and tickets.
25
+ - `associations` (default `false`): every association label and plain association between the object and the other objects under `objects`. Without it on either object of a pair, pull refreshes only the associations `hubspot/associations.ts` defines, and `--discover` lists the rest. `kalup init` sets it on every object it writes. HubSpot's own labels, and the plain association it defines between two standard objects, are never written.
25
26
 
26
27
  Under takeover (config.md), pull ends with a line naming what a plan for the target would archive once the files are as pull leaves them, such as what `--only` left out.
27
28
 
@@ -29,7 +30,7 @@ A portal property in scope is written unless `hubspot/removed.ts` names it or it
29
30
 
30
31
  ## Merge rules
31
32
 
32
- **Custom object schema**: its fields (config.md) take the portal value.
33
+ **Custom object schema**: its fields (config.md) take the portal value. A custom object config defines and the portal lacks is kept as the file has it, printed `missing in portal`; plan creates it. One `hubspot/removed.ts` names is never written back, with its pipelines, printed `in hubspot/removed.ts, not written back`, whether or not HubSpot still holds it. A key under `objects` that is neither a standard object, nor defined in config, nor in the portal is `E_UNKNOWN_OBJECT`.
33
34
 
34
35
  **Properties already in the file**:
35
36
 
@@ -48,6 +49,8 @@ A portal property in scope is written unless `hubspot/removed.ts` names it or it
48
49
 
49
50
  **Pipelines** merge by ID. A pipeline in both takes the portal's `label` and `displayOrder`; its stages take the portal's `label` and metadata field and follow the portal's order. A portal-only stage is added; a file-only stage is kept where it stood, printed `missing in portal`. A file pipeline the portal lacks is kept, printed `missing in portal`. With `pipelines: true`, a pipeline only the portal holds is appended to `hubspot/pipelines/<object>.ts`, the file created when needed, in `displayOrder` then ID order. Its export name is PascalCase of its label followed by `Pipeline` unless the label ends with it (`Sales Pipeline` to `SalesPipeline`); a stage's key is camelCase of its ID when the ID is a lowercase slug, else of its label, with `stage` in front of one starting with a digit. Names are made unique. The barrel re-exports every pipeline.
50
51
 
52
+ **Associations** merge by name. An entry in both takes the portal's `label` and `inverseLabel`, and keeps its key and comments; `inverseLabel` is left out when it equals the label. A file entry the portal lacks is kept, printed `missing in portal`. With `associations: true` on an object of the pair, an association only the portal holds is added to `hubspot/associations.ts`, the file created when needed, as `Associations`: from the object whose key sorts first, keyed camelCase of its name. The barrel re-exports `Associations`. A label HubSpot's schema read does not name yet, a few minutes after a create in the HubSpot UI, is not written until it does; one Kalup created is named by the type IDs state records.
53
+
51
54
  ## With state
52
55
 
53
56
  Where state holds agreed values for a resource (its base), each unit is compared with it, as `plan` does:
@@ -72,9 +75,9 @@ After the files are written, and only after a complete read, pull records in sta
72
75
  ## Flags
73
76
 
74
77
  - `--only <glob>`: merge only matching addresses. `*` matches any characters, `/` included: `property:companies/*`, `stage:deals/renewals/*`.
75
- - `--discover`: list what is outside the scope, write nothing: custom objects config does not name, properties, and the pipelines of each object without `pipelines: true` that the files do not define.
78
+ - `--discover`: list what is outside the scope, write nothing: custom objects config does not name, properties, the pipelines of each object without `pipelines: true` that the files do not define, and the associations between objects under `objects` that pull does not write.
76
79
  - `--check`: print the changes and `would write <file>`, write nothing. With `--exit-code`, exit 2 on any difference: a change line but `skipped`, `in hubspot/removed.ts`, a new property in a removed group, `config change kept` or `ignored on this target`, a `W_CODEC_MISMATCH`, or a file to rewrite.
77
- - `--json`: `data` holds `target`, `portalId`, `objects` (counts and `changes[]` per object: `kind`, `address`, and `field`, `before` and `after` when one field differs; a kept value has the file's side in `before`, the portal's in `after`; `kind` is `added`, `changed`, `missing`, `local-only`, `excluded`, `shadowed`, `removed`, `removed-group`, `kept`, `conflict`, `removed-in-hubspot`, `ignored` or `override-group`), `files` and `state` (`path`, `recorded`, the resources whose base changed, and `serial`; absent with `--check` or after an incomplete read); with `--discover`, what is outside the scope: `objects`, `properties` and `pipelines` (pipeline IDs by object).
80
+ - `--json`: `data` holds `target`, `portalId`, `objects` (counts and `changes[]` per object: `kind`, `address`, and `field`, `before` and `after` when one field differs; a kept value has the file's side in `before`, the portal's in `after`; `kind` is `added`, `changed`, `missing`, `local-only`, `excluded`, `shadowed`, `removed`, `removed-group`, `kept`, `conflict`, `removed-in-hubspot`, `ignored` or `override-group`), `files` and `state` (`path`, `recorded`, the resources whose base changed, and `serial`; absent with `--check` or after an incomplete read); with `--discover`, what is outside the scope: `objects`, `properties`, `pipelines` (pipeline IDs by object) and `associations` (addresses).
78
81
 
79
82
  ## Exit codes
80
83
 
package/docs/rm.md CHANGED
@@ -1,14 +1,15 @@
1
1
  # Remove
2
2
 
3
- `kalup rm <address> [--release] [--json]` takes a property, property group, pipeline or stage out of config and writes its tombstone in `hubspot/removed.ts`. Absence never deletes: a resource dropped from an object file by hand stays in the portal and comes back on the next pull. `rm` is the only way to ask for a delete, and it works offline: it reads no key, sends no request and never touches state.
3
+ `kalup rm <address> [--release] [--json]` takes a custom object, property, property group, pipeline, stage or association out of config and writes its tombstone in `hubspot/removed.ts`. Absence never deletes: a resource dropped from an object file by hand stays in the portal and comes back on the next pull. `rm` is the only way to ask for a delete, and it works offline: it reads no key, sends no request and never touches state.
4
4
 
5
5
  This page is the reference. For the walk-through with examples, see [kalup rm](https://kalup.dev/docs/commands/rm) on the website.
6
6
 
7
7
  ## What it writes
8
8
 
9
- - The address must be `property:<object>/<name>`, `group:<object>/<name>`, `pipeline:<object>/<id>` or `stage:<object>/<pipelineId>/<stageId>` (`E_TOMBSTONE_ADDRESS`, exit 3). Custom objects are not removed in this release.
9
+ - The address must be `object:<name>`, `property:<object>/<name>`, `group:<object>/<name>`, `pipeline:<object>/<id>`, `stage:<object>/<pipelineId>/<stageId>` or `association:<from>/<to>/<name>` (`E_TOMBSTONE_ADDRESS`, exit 3).
10
+ - A custom object leaves config with everything on it: its export, every pipeline export on the object and every association entry with the object on either side, and each file left with no export or entry goes. Its one tombstone covers its groups, properties, pipelines, stages and associations. Its entry under `objects` in `kalup.config.ts` stays, so the plan reads the object.
10
11
  - The property, or the group entry, leaves the export that defines it, in whichever file holds it. An export left with no properties keeps its groups; `defineObject` accepts it.
11
- - A pipeline's export leaves `hubspot/pipelines/<object>.ts`, and the file goes when it held no other. Its one tombstone covers its stages. A stage leaves its pipeline's `stages`.
12
+ - A pipeline's export leaves `hubspot/pipelines/<object>.ts`, and the file goes when it held no other. Its one tombstone covers its stages. A stage leaves its pipeline's `stages`. An association's entry leaves `hubspot/associations.ts`, and the file goes when it held no other.
12
13
  - `hubspot/removed.ts` gets `'<address>': { action: 'destroy' }`, or `'release'` with `--release`. An address already there gets its action changed; the same action writes nothing.
13
14
  - An address config does not define (an orphan the plan lists) gets the tombstone alone.
14
15
  - `hubspot/index.ts` is written again.
@@ -17,13 +18,13 @@ Before writing, rm validates the project as it would leave it; any issue is exit
17
18
 
18
19
  ## What it refuses
19
20
 
20
- - `destroy` for a resource with `lifecycle: { preventDestroy: true }` (`E_PREVENT_DESTROY`, exit 3). Remove preventDestroy first, or use `--release`.
21
+ - `destroy` for a resource with `lifecycle: { preventDestroy: true }` (`E_PREVENT_DESTROY`, exit 3), or for a custom object while anything on it sets it, since the archive takes them along. Remove preventDestroy first, or use `--release`.
21
22
  - A group that config properties use, or a property a custom object schema in config names as a display, required or searchable property (`E_RM_DEPENDENTS`, exit 3). This applies to `--release` too.
22
23
  - A pipeline's last stage, or a ticket pipeline's last closed stage (`E_PIPELINE_STAGES`, exit 3): HubSpot refuses both. Remove the pipeline instead, or mark another stage closed first.
23
24
 
24
25
  ## Destroy and release
25
26
 
26
- A `destroy` tombstone becomes a delete in the next plan only when all of these hold: the portal's state owns the resource (Kalup created or adopted it there), the target sets `allowDestroy: true`, and a person at a terminal types the target name and the number of destructive steps when applying (apply.md). HubSpot archives a deleted property; it can be restored in HubSpot for 90 days. A deleted pipeline or stage is gone for good: HubSpot keeps no archive, and refuses the delete while a record sits in the stage.
27
+ A `destroy` tombstone becomes a delete in the next plan only when all of these hold: the portal's state owns the resource (Kalup created or adopted it there), the target sets `allowDestroy: true`, and a person at a terminal types the target name and the number of destructive steps when applying (apply.md). HubSpot archives a deleted property; it can be restored in HubSpot for 90 days. HubSpot archives a deleted custom object with what is on it, and refuses while the object holds records; Kalup never purges it. A deleted pipeline or stage is gone for good: HubSpot keeps no archive, and refuses the delete while a record sits in the stage. A deleted association label is gone for good too, and records lose it; HubSpot refuses to delete a plain association while a label of its pair remains, so remove the labels first.
27
28
 
28
29
  A `release` tombstone stops Kalup managing the resource. The portal keeps it, the next apply drops its state entry without a request, and pull never writes it back into config (pull.md).
29
30
 
package/docs/snapshot.md CHANGED
@@ -7,7 +7,7 @@ This page is the reference. For the walk-through with examples, see [kalup snaps
7
7
  ## Order of work
8
8
 
9
9
  1. Validate (exit 3), pick the target (targets.md), the read key, then the portal guard (exit 4).
10
- 2. Pull's read and scope: three properties lists per object, and its pipelines when they are in scope, `skip` and `name` overrides applied. A 403 leaves that object unread; on the pipelines list, only its pipelines.
10
+ 2. Pull's read and scope: three properties lists per object, its pipelines when they are in scope, and the associations of each pair in association scope, `skip` and `name` overrides applied. A 403 leaves that object unread; on the pipelines list, only its pipelines; on a labels list, only that pair's associations.
11
11
  3. Write the file, then print a summary.
12
12
 
13
13
  ## The file
@@ -16,7 +16,7 @@ By default the file is `.kalup/snapshots/<target>/<stamp>.json` under the projec
16
16
 
17
17
  The file is an `ir/1` document with `generator.frontend: 'portal'`, keys sorted, and U+007F to U+009F, U+2028 and U+2029 escaped:
18
18
 
19
- - `resources`: what the read captured, under local addresses. Groups carry their label. A custom property carries its definition; a HubSpot-defined or calculated one at most its options. A custom object carries its labels, display property and property lists. Where one names a portal resource a `name` override shadows (a property's group, a schema's property), it reads `shadowed:<name>`, which never equals a config name.
19
+ - `resources`: what the read captured, under local addresses. Groups carry their label. A custom property carries its definition; a HubSpot-defined or calculated one at most its options. A custom object carries its labels, description, display property and property lists. Where one names a portal resource a `name` override shadows (a property's group, a schema's property), it reads `shadowed:<name>`, which never equals a config name.
20
20
  - `observation`: the target's name and `portalId`, `observedAt`, the one timestamp an IR document may hold, and `coverage`.
21
21
 
22
22
  `coverage` has `complete`, `otherObjects` (custom objects config does not name, or `unknown` when the schemas list was not read), `notCaptured` (documented fields Kalup drops) and one entry per object key: `status` (`read`, `unreadable`, `absent` or `excluded`), `missingScope` when unreadable, `objectTypeId`, the names out of scope, `unaddressable` (config names them), shadowed by a `name` override or unsupported (Kalup does not write it; `externalOptions` and `referencedObjectType` are kept), `unsupportedSchema` for a schema without a label, and the addresses `skip` and `name` overrides exclude or rename.
@@ -40,7 +40,7 @@ Snapshot of target sandbox, portal 1111111, observed at 2026-09-23T10:15:30.123Z
40
40
  Wrote .kalup/snapshots/sandbox/20260923T101530123Z.json
41
41
  ```
42
42
 
43
- `--json` gives `file`, `target`, `portalId`, `observedAt`, `complete` and `counts` (objects read, groups, properties, pipelines and stages held). The text names pipelines and stages only when the snapshot holds any.
43
+ `--json` gives `file`, `target`, `portalId`, `observedAt`, `complete` and `counts` (objects read, groups, properties, pipelines, stages and associations held). The text names pipelines, stages and associations only when the snapshot holds any.
44
44
 
45
45
  ## Exit codes
46
46
 
package/docs/targets.md CHANGED
@@ -45,7 +45,7 @@ The key goes out as `Authorization: Bearer`. `init` prints the read scopes the p
45
45
  `overrides` is keyed by address: `property:<object>/<name>`, `group:<object>/<name>` or `object:<name>`. A key that is not an address in config is `E_UNKNOWN_OVERRIDE`. Any field other than these four is `E_NOT_DATA`:
46
46
 
47
47
  - `name: '<portal name>'`: the resource's internal name in this portal, or for a pipeline or stage its ID there. Every read uses it; `plan` blocks it when the portal lacks it. `E_OVERRIDE_AMBIGUOUS` when the portal holds both names; `E_OVERRIDE_NAME` when two addresses would read one portal resource.
48
- - `skip: true`: left out on this target by every read, a group with its config properties, a pipeline with its stages.
48
+ - `skip: true`: left out on this target by every read, a group with its config properties, a pipeline with its stages, an association alone.
49
49
  - `definition: {...}`: the fields that differ on this target (config.md).
50
50
  - `lookup`: no lookup resource is managed yet; `plan` blocks the resource, `compare` reports it unknown.
51
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kalup",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Configuration as code for HubSpot: pull, compare, plan and apply properties and property groups",
5
5
  "keywords": [
6
6
  "hubspot",
@@ -63,7 +63,7 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@oclif/core": "^5.1.2",
66
- "@kalup/core": "0.4.0"
66
+ "@kalup/core": "0.5.0"
67
67
  },
68
68
  "engines": {
69
69
  "node": ">=22.13.1"