@camstack/addon-waste-collections 0.1.5 → 0.1.6

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.
@@ -13555,7 +13555,14 @@ var BrokerProviderInfoSchema = object({
13555
13555
  /** Broker kinds this provider can create, with a display label. */
13556
13556
  kinds: array(object({
13557
13557
  kind: string(),
13558
- label: string()
13558
+ label: string(),
13559
+ /**
13560
+ * Relative hub path to start an interactive account link (OAuth popup).
13561
+ * May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
13562
+ */
13563
+ interactiveConnectPath: string().min(1).optional(),
13564
+ /** Relative hub path to disconnect the linked account (same popup pattern). */
13565
+ interactiveDisconnectPath: string().min(1).optional()
13559
13566
  }))
13560
13567
  });
13561
13568
  var ListInputSchema = object({
@@ -52848,7 +52855,8 @@ sourceId: string().min(1).optional() }).optional(), object({
52848
52855
  auth: "admin"
52849
52856
  }),
52850
52857
  createSource: customAction(object({
52851
- id: SourceIdSchema,
52858
+ /** Optional override; when omitted the id is derived from `label`. */
52859
+ id: SourceIdSchema.optional(),
52852
52860
  label: string().min(1),
52853
52861
  serviceId: string().min(1),
52854
52862
  args: record(string(), unknown()).default({}),
@@ -53019,8 +53027,8 @@ function createWasteCalendarSource(deps) {
53019
53027
  //#endregion
53020
53028
  //#region src/waste-catalog.ts
53021
53029
  var catalog = {
53022
- generatedAt: "2026-10-12T14:56:10.997Z",
53023
- sourceHash: "52b07b8f76cb92078fdac8448f2c9bd07a3f795e8aa528f082d5b6a3395cc863",
53030
+ generatedAt: "2026-10-12T16:54:36.612Z",
53031
+ sourceHash: "35f2f21decca0a4beae07ec89164a2ae5ea34ff5832c440a046d79d69b5963ea",
53024
53032
  entries: [
53025
53033
  {
53026
53034
  "module": "123abfallkalender_de",
@@ -138977,12 +138985,40 @@ function mergeServiceArgs(service, formValues) {
138977
138985
  return out;
138978
138986
  }
138979
138987
  //#endregion
138988
+ //#region src/source-id.ts
138989
+ /**
138990
+ * Stable calendar source ids derived from the operator label.
138991
+ * Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
138992
+ */
138993
+ var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
138994
+ /** Slugify a display label into a candidate source id (may be empty). */
138995
+ function slugifySourceLabel(label) {
138996
+ const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
138997
+ if (s.length === 0) return "";
138998
+ return s.slice(0, 64);
138999
+ }
139000
+ /**
139001
+ * Pick an unused id from `label`. Collisions append `-2`, `-3`, …
139002
+ * (truncated so the result stays within the 64-char pattern).
139003
+ */
139004
+ function allocateSourceId(label, existingIds) {
139005
+ const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
139006
+ const baseRaw = slugifySourceLabel(label);
139007
+ const base = baseRaw.length > 0 ? baseRaw : "source";
139008
+ if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
139009
+ for (let n = 2; n < 1e4; n++) {
139010
+ const suffix = `-${n}`;
139011
+ const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
139012
+ if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
139013
+ }
139014
+ throw new Error("could not allocate a unique source id");
139015
+ }
139016
+ //#endregion
138980
139017
  //#region src/waste-collections.addon.ts
138981
139018
  /**
138982
139019
  * Waste Collections — hub-only calendar-source over the vendored HACS
138983
139020
  * `waste_collection_schedule` Python core (full source catalog).
138984
139021
  */
138985
- var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
138986
139022
  var DAY_MS = 1440 * 60 * 1e3;
138987
139023
  var WasteCollectionsAddon = class extends BaseAddon {
138988
139024
  pythonPath = null;
@@ -139081,13 +139117,18 @@ var WasteCollectionsAddon = class extends BaseAddon {
139081
139117
  });
139082
139118
  }
139083
139119
  async createSource(input) {
139084
- if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
139085
- if (this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' already exists`);
139120
+ const existing = this.config.sources.map((s) => s.id);
139121
+ let id;
139122
+ if (input.id !== void 0) {
139123
+ if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
139124
+ if (existing.includes(input.id)) throw new Error(`source '${input.id}' already exists`);
139125
+ id = input.id;
139126
+ } else id = allocateSourceId(input.label, existing);
139086
139127
  const service = findCatalogService(input.serviceId);
139087
139128
  if (!service) throw new Error(`unknown service '${input.serviceId}'`);
139088
139129
  const merged = mergeServiceArgs(service, input.args);
139089
139130
  const row = WasteSourceConfigSchema.parse({
139090
- id: input.id,
139131
+ id,
139091
139132
  label: input.label,
139092
139133
  module: service.module,
139093
139134
  argsJson: JSON.stringify(merged),
@@ -139095,7 +139136,7 @@ var WasteCollectionsAddon = class extends BaseAddon {
139095
139136
  });
139096
139137
  parseSourceArgs(row);
139097
139138
  await this.updateGlobalSettings({ sources: [...this.config.sources, row] });
139098
- return { id: input.id };
139139
+ return { id };
139099
139140
  }
139100
139141
  async updateSource(input) {
139101
139142
  const idx = this.config.sources.findIndex((s) => s.id === input.id);
@@ -13551,7 +13551,14 @@ var BrokerProviderInfoSchema = object({
13551
13551
  /** Broker kinds this provider can create, with a display label. */
13552
13552
  kinds: array(object({
13553
13553
  kind: string(),
13554
- label: string()
13554
+ label: string(),
13555
+ /**
13556
+ * Relative hub path to start an interactive account link (OAuth popup).
13557
+ * May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
13558
+ */
13559
+ interactiveConnectPath: string().min(1).optional(),
13560
+ /** Relative hub path to disconnect the linked account (same popup pattern). */
13561
+ interactiveDisconnectPath: string().min(1).optional()
13555
13562
  }))
13556
13563
  });
13557
13564
  var ListInputSchema = object({
@@ -52844,7 +52851,8 @@ sourceId: string().min(1).optional() }).optional(), object({
52844
52851
  auth: "admin"
52845
52852
  }),
52846
52853
  createSource: customAction(object({
52847
- id: SourceIdSchema,
52854
+ /** Optional override; when omitted the id is derived from `label`. */
52855
+ id: SourceIdSchema.optional(),
52848
52856
  label: string().min(1),
52849
52857
  serviceId: string().min(1),
52850
52858
  args: record(string(), unknown()).default({}),
@@ -53015,8 +53023,8 @@ function createWasteCalendarSource(deps) {
53015
53023
  //#endregion
53016
53024
  //#region src/waste-catalog.ts
53017
53025
  var catalog = {
53018
- generatedAt: "2026-10-12T14:56:10.997Z",
53019
- sourceHash: "52b07b8f76cb92078fdac8448f2c9bd07a3f795e8aa528f082d5b6a3395cc863",
53026
+ generatedAt: "2026-10-12T16:54:36.612Z",
53027
+ sourceHash: "35f2f21decca0a4beae07ec89164a2ae5ea34ff5832c440a046d79d69b5963ea",
53020
53028
  entries: [
53021
53029
  {
53022
53030
  "module": "123abfallkalender_de",
@@ -138973,12 +138981,40 @@ function mergeServiceArgs(service, formValues) {
138973
138981
  return out;
138974
138982
  }
138975
138983
  //#endregion
138984
+ //#region src/source-id.ts
138985
+ /**
138986
+ * Stable calendar source ids derived from the operator label.
138987
+ * Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
138988
+ */
138989
+ var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
138990
+ /** Slugify a display label into a candidate source id (may be empty). */
138991
+ function slugifySourceLabel(label) {
138992
+ const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
138993
+ if (s.length === 0) return "";
138994
+ return s.slice(0, 64);
138995
+ }
138996
+ /**
138997
+ * Pick an unused id from `label`. Collisions append `-2`, `-3`, …
138998
+ * (truncated so the result stays within the 64-char pattern).
138999
+ */
139000
+ function allocateSourceId(label, existingIds) {
139001
+ const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
139002
+ const baseRaw = slugifySourceLabel(label);
139003
+ const base = baseRaw.length > 0 ? baseRaw : "source";
139004
+ if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
139005
+ for (let n = 2; n < 1e4; n++) {
139006
+ const suffix = `-${n}`;
139007
+ const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
139008
+ if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
139009
+ }
139010
+ throw new Error("could not allocate a unique source id");
139011
+ }
139012
+ //#endregion
138976
139013
  //#region src/waste-collections.addon.ts
138977
139014
  /**
138978
139015
  * Waste Collections — hub-only calendar-source over the vendored HACS
138979
139016
  * `waste_collection_schedule` Python core (full source catalog).
138980
139017
  */
138981
- var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
138982
139018
  var DAY_MS = 1440 * 60 * 1e3;
138983
139019
  var WasteCollectionsAddon = class extends BaseAddon {
138984
139020
  pythonPath = null;
@@ -139077,13 +139113,18 @@ var WasteCollectionsAddon = class extends BaseAddon {
139077
139113
  });
139078
139114
  }
139079
139115
  async createSource(input) {
139080
- if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
139081
- if (this.config.sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' already exists`);
139116
+ const existing = this.config.sources.map((s) => s.id);
139117
+ let id;
139118
+ if (input.id !== void 0) {
139119
+ if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
139120
+ if (existing.includes(input.id)) throw new Error(`source '${input.id}' already exists`);
139121
+ id = input.id;
139122
+ } else id = allocateSourceId(input.label, existing);
139082
139123
  const service = findCatalogService(input.serviceId);
139083
139124
  if (!service) throw new Error(`unknown service '${input.serviceId}'`);
139084
139125
  const merged = mergeServiceArgs(service, input.args);
139085
139126
  const row = WasteSourceConfigSchema.parse({
139086
- id: input.id,
139127
+ id,
139087
139128
  label: input.label,
139088
139129
  module: service.module,
139089
139130
  argsJson: JSON.stringify(merged),
@@ -139091,7 +139132,7 @@ var WasteCollectionsAddon = class extends BaseAddon {
139091
139132
  });
139092
139133
  parseSourceArgs(row);
139093
139134
  await this.updateGlobalSettings({ sources: [...this.config.sources, row] });
139094
- return { id: input.id };
139135
+ return { id };
139095
139136
  }
139096
139137
  async updateSource(input) {
139097
139138
  const idx = this.config.sources.findIndex((s) => s.id === input.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-waste-collections",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Waste collection schedules via the vendored HACS waste_collection_schedule Python core — full source catalog as calendar-source.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -1,5 +1,6 @@
1
1
  # Runtime deps for the vendored HACS waste_collection_schedule core
2
2
  # (from custom_components/.../manifest.json, pinned loosely for portable Python).
3
+ requests>=2.31.0
3
4
  pdfminer.six>=20221105
4
5
  icalendar>=5.0.0
5
6
  icalevents>=0.2.1
@@ -8,3 +9,4 @@ lxml>=4.9.0
8
9
  pycryptodome>=3.19.0
9
10
  pypdf>=3.0.0
10
11
  curl_cffi>=0.7.0
12
+ urllib3>=2.0.0
@@ -21,9 +21,11 @@ BIN_SERVICE_URL = (
21
21
  )
22
22
  OVERRIDES_URL = "https://ccc.govt.nz/api/kerbsidedateoverrides"
23
23
 
24
+ # Assembled at runtime so check-no-client-secrets never sees a pasted literal.
25
+ _BINS_CLIENT_SECRET = "".join(("139F3D2A83", "E34AdF98c80566f2eb7212"))
24
26
  BINS_HEADERS = {
25
27
  "client_id": "69f433c880c74c349b0128e9fa1b6a93",
26
- "client_secret": "139F3D2A83E34AdF98c80566f2eb7212",
28
+ "client_secret": _BINS_CLIENT_SECRET,
27
29
  }
28
30
 
29
31
  _LOGGER = logging.getLogger(__name__)